· 5 years ago · May 27, 2020, 06:10 PM
1import { Gender, prisma, User, KudaLog } from "../schema/generated/prisma-client";
2const dateFormat = require("dateformat");
3const bcrypt = require("bcryptjs");
4import { sign } from "jsonwebtoken";
5import { salt } from "../const";
6import mail from "../functions/mail";
7import { verifyToken } from "../middleware/utils";
8require("dotenv").config();
9
10const fs = require("fs");
11const Kuda = require('kuda-node');
12
13const publicKey = fs.readFileSync(__dirname + "/key/publickey.xml"); // or path to your kuda public key
14const privateKey = fs.readFileSync(__dirname + "/key/31BGNW8Y2eIb6nw49oOr.xml"); // or path to your kuda kuda private key
15const clientKey = "31BGNW8Y2eIb6nw49oOr"; // name of private key file without the .xml suffix (extension)
16
17const kuda = Kuda({
18 publicKey,
19 privateKey,
20 clientKey
21});
22
23/**
24 * Function to validate email address
25 * @param {String} email {Required} The email to be validated
26 * @return {Boolean} A Boolean value indicating if the email is valid
27 * N/B
28 * This isn't fool proof and there is no fool proof way of
29 * validating an email address.
30 */
31const validateEmail = (email: string) => {
32 const pattern = /^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/;
33 if (!email.match(pattern)) {
34 throw new Error("Email address not supported");
35 }
36};
37
38
39
40const createVirtualAccount = async function kudaCreateAccount(
41 email,
42 phoneNumber,
43 firstName,
44 lastName,
45 trackingReference
46) {
47 try {
48 // console.log(firstName, lastName)
49 const response = await kuda({
50 serviceType: "CREATE_VIRTUAL_ACCOUNT",
51 requestRef: Math.floor(Math.random() * 1000000000000 + 1),
52 data: { email, phoneNumber, firstName, lastName, trackingReference }
53 });
54 // console.log("Kuda Virtual Created \n", response.Data);
55 return response;
56 } catch (error) {
57 console.error("Error Kuda Virtual Creation", error);
58 }
59 return true;
60}
61
62// Retrieve virtual account
63const retrieveVirtualAccount = async trackingReference => {
64 kuda(
65 {
66 serviceType: "RETRIEVE_VIRTUAL_ACCOUNT",
67 requestRef: Math.floor(Math.random() * 1000000000000 + 1), // you can generate your random number your own way. This is just an example.
68 data: {
69 trackingReference: trackingReference // you can generate your trackingReference some other way you choose.
70 }
71 },
72 data => {
73 // data => decrypted response from Kuda API
74 // do anything with your data
75 console.log(JSON.stringify(data, null, 2));
76 return JSON.stringify(data, null, 2);
77 }
78 );
79};
80
81const nameEnquiry = async (
82 beneficiaryAccountNumber, // beneficiary accont number
83 bankCode,
84 senderTrackingReference // sender tracking reference
85) => {
86 kuda(
87 {
88 serviceType: "NAME_ENQUIRY",
89 requestRef: Math.floor(Math.random() * 1000000000000 + 1), // you can generate your random number your own way. This is just an example.
90 data: {
91 beneficiaryAccountNumber: beneficiaryAccountNumber,
92 bankCode: bankCode,
93 senderTrackingReference: senderTrackingReference
94 }
95 },
96 data => {
97 // data => decrypted response from Kuda API
98 // do anything with your data
99 console.log(JSON.stringify(data, null, 2));
100 return data.Data.nameEnquiryID;
101 }
102 );
103};
104
105export async function queryTrackingReference(trackingReference: string) {
106
107 const trackingRf = await prisma.user({
108 trackingReference
109 });
110 // If it is null, does not exist
111 if (trackingRf == null) {
112 return {
113 trackingReference,
114 status: "false"
115 };
116 }
117 // If it is not null, exists
118 if (trackingRf != null) {
119 return {
120 trackingReference,
121 status: "true"
122 };
123 }
124}
125
126const validateTransactionPin = (pin: string, repeatPin: string) => {
127 const pattern = /^[0-9]*$/gm;
128 if (pin.length !== 4) {
129 throw new Error("Invalid transaction pin");
130 }
131 if (pin !== repeatPin) {
132 throw new Error("Your pin is not the same");
133 }
134 if (!pin.match(pattern)) {
135 throw new Error("Invalid transaction pin");
136 }
137};
138
139const validateFullName = (fullname: string) => {
140 const pattern1 = /^[a-zA-Z]+ [a-zA-Z]+ [a-zA-Z]+$/;
141 const pattern2 = /^[a-zA-Z]+ [a-zA-Z]+$/;
142 if (fullname.match(pattern1) || fullname.match(pattern2)) {
143 return true;
144 } else {
145 throw new Error("Invalid name");
146 }
147};
148
149export async function authenticateUser(phonenumber: string) {
150 // check if phone number is an actual phone number
151 if (phonenumber.length < 11) {
152 throw new Error("Your phone number is incomplete");
153 }
154 // convert phone number to +234 format
155 if (phonenumber.startsWith("0")) {
156 let tel = phonenumber;
157 phonenumber = "+234" + tel.substr(1);
158 }
159 const user = await prisma.user({
160 phonenumber
161 });
162 if (user == null) {
163 return {
164 phonenumber,
165 status: "register"
166 };
167 }
168 if (!user.verified) {
169 return {
170 phonenumber,
171 status: "verify"
172 };
173 }
174 return {
175 phonenumber,
176 status: "login"
177 };
178}
179
180export async function createUser(data: User) {
181 // extract the date of birth and password for modifications
182 let { DOB, password, email, phonenumber, fullname, profile_picture, trackingReference } = data;
183 // let { timestamp, endpoint, status, message } = kudaLogData;
184 // check if phone number is an actual phone number
185 if (phonenumber.length < 11) {
186 throw new Error("Your phone number is incomplete");
187 }
188
189 const splitFullname = fullname.split(' ');
190 let firstname = splitFullname[0];
191 let lastname = splitFullname[1];
192
193 // validate the geniunity of email address
194 await validateEmail(email);
195
196 // validate users name
197 await validateFullName(fullname);
198
199 // convert phone number to +234 format
200 if (phonenumber.startsWith("0")) {
201 let tel = phonenumber;
202 phonenumber = "+234" + tel.substr(1);
203 }
204
205 // check if password is greater than 7 characters
206 if (password.length < 8) {
207 throw new Error("Your password should be greater than 7 characters");
208 }
209
210 let formatDate;
211 // format the date
212 formatDate = dateFormat(DOB, "yyyy-mm-dd HH:MM:ss");
213 // put in a format the database understands
214 formatDate = new Date(formatDate);
215 // hash password
216 password = bcrypt.hashSync(password, salt);
217
218 // add it back to the data object
219 data.DOB = formatDate;
220 data.password = password;
221 data.phonenumber = phonenumber;
222 data.trackingReference = trackingReference;
223
224 const user = await prisma.createUser(data);
225
226 const virtual_account = await createVirtualAccount(email, phonenumber, firstname, lastname, trackingReference);
227
228 // Kuda log data
229
230 let logData = {
231 timestamp: new Date().toISOString(),
232 endpoint: 'CREATE_VIRTUAL_ACCOUNT',
233 status: virtual_account.Status,
234 message: virtual_account.Message
235 }
236
237 // console.log('====================================');
238 // console.log(logData);
239 // console.log('====================================');
240 // console.log(virtual_account);
241
242 await prisma.createKudaLog(logData);
243
244 return sendVerificationCode(user.phonenumber);
245}
246
247export async function setTransactionPin(
248 token: string,
249 // tslint:disable-next-line: variable-name
250 transaction_pin: string,
251 repeat_transaction_pin: string
252) {
253 // validate the transaction pin
254 await validateTransactionPin(transaction_pin, repeat_transaction_pin);
255 // hash transaction pin
256 transaction_pin = bcrypt.hashSync(transaction_pin, salt);
257}
258
259export async function sendVerificationCode(phonenumber: string, code?: number) {
260 // convert phone number to +234 format
261 if (phonenumber.startsWith("0")) {
262 let tel = phonenumber;
263 phonenumber = "+234" + tel.substr(1);
264 }
265 const user = await prisma.user({
266 phonenumber
267 });
268 // check if it has been verified
269 if (user.verified) {
270 throw new Error("User has been verified");
271 }
272 // generate a 6 digit number for verification if code is not present
273 if (!code) {
274 code = Math.floor(Math.random() * 900000) + 100000;
275 }
276 if (String(code).length !== 6) {
277 throw new Error("Code must be 6 digits");
278 }
279
280 // check if an activation Code exists before for the user
281 const verificationCodes = await prisma.verificationCodes({
282 where: {
283 user: {
284 phonenumber
285 }
286 }
287 });
288
289 let verificationCode;
290 // if it exists delete the verification code
291 if (verificationCodes.length > 0) {
292 verificationCode = verificationCodes[0];
293 await prisma.deleteVerificationCode({
294 id: verificationCode.id
295 });
296 }
297
298 // create the verification
299 const newVerificationCode = await prisma.createVerificationCode({
300 code,
301 user: {
302 connect: {
303 phonenumber
304 }
305 }
306 });
307
308 const template = `
309 <html>
310
311 <head>
312 <title></title>
313 <link href='https://fonts.googleapis.com/css?family=Rubik' rel='stylesheet'>
314 <style>
315 @import url('https://fonts.googleapis.com/css?family=Rubik');
316 </style>
317 </head>
318
319 <body>
320 <div style="background-color:#f1f3f5;color:#233858;font-family:'Rubik';margin:0">
321 <div style="background-color:white;display:block;margin:0 auto;max-width:720px">
322 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
323 <tbody>
324 <tr>
325 <td
326 style="font-size:0!important;line-height:100%;padding-left:16px;padding-right:16px;padding-top:36px;text-align:center">
327
328 <div
329 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
330 <table width="100%" border="0" cellspacing="0" cellpadding="0"
331 style="min-width:100%;width:100%">
332 <tbody>
333 <tr>
334 <td
335 style="line-height:100%;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
336 <a href="#m_2661994423616798564_m_222366038327339295_"
337 style="color:#233858;font-family:'Rubik';text-decoration:underline">
338 <img src="https://img.techpowerup.org/200427/vector.png" width="64"
339 height="24" style="height:auto;max-width:200px;width:auto"
340 class="CToWUd">
341 <br />
342 <img src="https://img.techpowerup.org/200427/frame.png" width="64"
343 height="24"
344 style="height:auto;max-width:65px;width:auto;margin-top: 5px;"
345 class="CToWUd">
346 </a>
347 </td>
348 </tr>
349 </tbody>
350 </table>
351 <table align="center" width="100%" border="0" cellspacing="0" cellpadding="0"
352 style="display:table;margin-left:auto;margin-right:auto">
353 <tbody>
354 <tr>
355 <td
356 style="background-color:transparent!important;border-bottom:1px solid rgba(60,60,60,0.07);border-bottom-color:rgba(60,60,60,0.07);font-size:0;height:2px;line-height:1px;min-height:1px;overflow:hidden;padding-bottom:24px">
357 </td>
358 </tr>
359 </tbody>
360 </table>
361 </div>
362
363 </td>
364 </tr>
365 </tbody>
366 </table>
367
368 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
369 <tbody>
370 <tr>
371 <td
372 style="font-size:0!important;line-height:100%;padding-left:8px;padding-right:8px;text-align:center">
373
374 <div
375 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
376 <table width="100%" border="0" cellspacing="0" cellpadding="0"
377 style="border:2px solid;border-color:transparent;border-radius:6px;min-width:100%;width:100%">
378 <tbody>
379 <tr>
380 <td
381 style="background-position:50% 0;background-repeat:no-repeat;border:0;border-radius:3px;min-width:100%;padding-left:8px;padding-right:8px;padding-top:32px;text-align:left;vertical-align:top;width:100%">
382 <h1
383 style="color: #000000;text-align:center;font-family:'Rubik'!important;font-size:22px;font-weight:800;line-height:44px;margin-bottom:14px;margin-left:0;margin-right:0;margin-top:32px;padding:0">
384 Complete Your Registration Request</h1>
385 <p
386 style="color: #82859A;text-align:center;font-family:'Rubik';font-size:12px;line-height:1.6;margin-bottom:30px;margin-top:0;">
387 Dear ${user.fullname}, Please use the OTP: <span
388 style="color: #F26A21;">${newVerificationCode.code}</span> to complete your
389 registration</p>
390
391 <p
392 style="color: #82859A;font-family:'Rubik';font-size:13px;line-height:1.6;margin-top:0;padding-top:36px">
393 Thanks,</p>
394 <p
395 style="color: #82859A;font-family:'Rubik';font-size:13px!important;margin-top:10px">
396 The Ebucks Team.</p>
397 </td>
398 </tr>
399 </tbody>
400 </table>
401 </div>
402
403 </td>
404 </tr>
405 </tbody>
406 </table>
407
408 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
409 <tbody>
410 <tr>
411 <td
412 style="font-size:0!important;line-height:100%;padding-left:8px;padding-right:8px;text-align:center">
413 <div
414 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
415 <table width="100%" border="0" cellspacing="0" cellpadding="0"
416 style="border:2px solid;border-color:transparent;border-radius:6px;min-width:100%;width:100%">
417 <tbody>
418 <tr>
419 <td
420 style="background-position:50% 0;background-repeat:no-repeat;border:0;border-radius:3px;min-width:100%;padding-left:8px;padding-right:8px;padding-top:32px;text-align:left;vertical-align:top;width:100%">
421 <p
422 style="border-top:1px solid rgba(60,60,60,0.07);color: #82859A;font-family:'Rubik';font-size:13px;letter-spacing: 0.02em;line-height:1.6;margin-bottom:20px;margin-top:100px;padding-top:20px">
423 Fund Wallet & Purchase with ease with our app today, availabe on
424 <a href="#">Android</a> and <a href="#">iOS</a> (Coming Soon) </p>
425 </td>
426 </tr>
427 </tbody>
428 </table>
429 </div>
430
431 </td>
432 </tr>
433 </tbody>
434 </table>
435
436
437
438 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
439 <tbody>
440 <tr>
441 <td
442 style="background-color:#0041C4;display:block;font-size:0!important;height:270px;line-height:100%;margin-top:20px;padding-left:8px;padding-right:8px;text-align:center">
443
444 <div
445 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
446 <table width="100%" border="0" cellspacing="0" cellpadding="0"
447 style="min-width:100%;width:100%">
448 <tbody>
449 <tr>
450 <td
451 style="min-width:100%;padding-left:8px;padding-right:8px;vertical-align:top;width:100%">
452 <div
453 style="display:inline-block;float:left;font-size:0;min-width:0!important;vertical-align:top;width:25%">
454 <table width="100%" border="0" cellspacing="0" cellpadding="0"
455 style="min-width:100%;padding-top:32px;width:100%">
456 <tbody>
457 <tr>
458 <td
459 style="color:#233858;font-size:16px;line-height:100%;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
460 <a href="#"
461 style="color:#233858;font-family:'Rubik';text-decoration:underline">
462 <img src="https://img.techpowerup.org/200427/frame-1.png"
463 width="64" height="24"
464 style="height:24px;max-width:200px;width:auto;"
465 class="CToWUd"> <br />
466 <img src="https://img.techpowerup.org/200427/frame-2.png"
467 width="64" height="24"
468 style="height:auto;max-width:65px;width:auto;margin-top: 5px;"
469 class="CToWUd">
470 </a>
471 </td>
472 </tr>
473 </tbody>
474 </table>
475 </div>
476 <div
477 style="display:inline-block;float:right;font-size:0;min-width:0!important;vertical-align:top;width:25%">
478 <table width="100%" border="0" cellspacing="0" cellpadding="0"
479 style="margin-left:auto;margin-right:0;min-width:100%;padding-top:32px;text-align:right;width:100%">
480 <tbody>
481 <tr>
482 <td style="color:#233858;display:inline;font-size:16px;min-width:100%;padding-left:8px;padding-right:8px;vertical-align:top;width:100%; display: flex;
483 flex-direction: column;align-items: center;">
484 <a href="#"
485 style="color:#233858;font-family:'Rubik';text-decoration:underline; margin-top: 15px;"
486 target="_blank" data-saferedirecturl="#"><img
487 src="https://ci6.googleusercontent.com/proxy/QibhghTDk6b0hIfNEbXhPkXgN1dvldl09KWTEGXqVOqUVuIUMbye4bx_nb1fo3kXVlLdOqShZs8peXzMvw=s0-d-e1-ft#https://emery.cloudns.cx/static/img/tw.png"
488 alt="myebucks Twitter"
489 style="height:16px;margin-left:20px;max-width:200px;width:auto"
490 class="CToWUd"></a>
491 <a href="#"
492 style="color:#233858;font-family:'Rubik';text-decoration:underline; margin-top: 15px;"
493 target="_blank" data-saferedirecturl="#"><img
494 src="https://ci5.googleusercontent.com/proxy/D1G7umjs3HsEa9BlJ6D8ZYRJYHUIrFdZrWaLNQiUfInBYUQmHzcjoEsaCqSoyzFUzEQ6F8n2lT0N_2Mf2A=s0-d-e1-ft#https://emery.cloudns.cx/static/img/ig.png"
495 alt="myebucks Instagram"
496 style="height:16px;margin-left:20px;max-width:200px;width:auto"
497 class="CToWUd"></a>
498 <a href="#"
499 style="color:#233858;font-family:'Rubik';text-decoration:underline; margin-top: 15px;"
500 target="_blank" data-saferedirecturl="#"><img
501 src="https://ci3.googleusercontent.com/proxy/aVFBIC7qxNzk6PsoiyvWZjHrD6VrlqJ_aUP_yC3eiSLDZmAf2ID-qBV9sJDs7UUlwcgdEfkOCYFg15JaYA=s0-d-e1-ft#https://emery.cloudns.cx/static/img/fb.png"
502 alt="myebucks Facebook"
503 style="height:16px;margin-left:20px;max-width:200px;width:auto"
504 class="CToWUd"></a>
505 </td>
506 </tr>
507 </tbody>
508 </table>
509 </div>
510
511 <div
512 style="display:inline-block;float:left;font-size:0;min-width:0!important;vertical-align:top;width:25%">
513 <table width="100%" border="0" cellspacing="0" cellpadding="0"
514 style="min-width:100%;padding-top:32px;width:100%">
515 <tbody>
516 <tr>
517 <td
518 style="color:#233858;font-size:16px;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
519 <a href=""
520 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
521 target="_blank">Links</a>
522 <a href=""
523 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
524 target="_blank">About Us</a>
525 <a href=""
526 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
527 target="_blank">Careers</a>
528 <a href=""
529 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
530 target="_blank">Contact</a>
531 </td>
532 </tr>
533 </tbody>
534 </table>
535 </div>
536 <div
537 style="display:inline-block;float:right;font-size:0;min-width:0!important;vertical-align:top;width:25%">
538 <table width="100%" border="0" cellspacing="0" cellpadding="0"
539 style="min-width:100%;padding-top:32px;width:100%">
540 <tbody>
541 <tr>
542 <td
543 style="color:#233858;font-size:16px;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
544 <p
545 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
546 Office</p>
547 <p
548 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
549 23 Adekunle Akala Street,</p>
550 <p
551 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
552 Ajao Estate, Oshodi/Isolo.</p>
553 <p
554 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
555 Lagos State</p>
556 </td>
557 </tr>
558 </tbody>
559 </table>
560 </div>
561
562
563
564 <table align="center" width="100%" border="0" cellspacing="0"
565 cellpadding="0"
566 style="display:table;margin-left:auto;margin-right:auto;padding-top:32px">
567 <tbody>
568 <tr>
569 <td
570 style="background-color:transparent!important;border-bottom:1px solid rgba(255,255,255,0.07);color:#233858;font-size:16px;height:2px;line-height:1px;min-height:1px;overflow:hidden">
571 </td>
572 </tr>
573 </tbody>
574 </table>
575 </td>
576 </tr>
577 </tbody>
578 </table>
579 </div>
580
581 </td>
582 </tr>
583 </tbody>
584 </table>
585
586 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width: 100%; width: 100%;">
587 <tbody>
588 <tr>
589 <td
590 style="display: block; font-size: 0 !important;height: 20px;margin-top: 20px;padding-left: 8px;padding-right: 8px;">
591 <div
592 style="display:inline-block;float:left;font-size:0;min-width:0!important;vertical-align:top;">
593 <table width="100%" border="0" cellspacing="0" cellpadding="0"
594 style="min-width:100%;width:100%">
595 <tbody>
596 <tr>
597 <td>
598 <p style="color: #595959;font-family: Rubik;
599 font-style: normal;
600 font-weight: bold;
601 font-size: 12px;
602 line-height: 12px;">Powered By <span><img width="64" height="24"
603 style="height:auto;width:auto;"
604 src="https://img.techpowerup.org/200503/group-135.png" /></span>
605 </p>
606 </td>
607 </tr>
608 </tbody>
609 </table>
610 </div>
611 </td>
612 </tr>
613 </tbody>
614 </table>
615 <div class="yj6qo"></div>
616 <div class="adL">
617 </div>
618 </div>
619 <div class="adL">
620 </div>
621 </div>
622 </body>
623
624 </html>`;
625
626 await mail({
627 user,
628 // message: `Hello ${user.fullname}. Welcome to Payment App. Enter this ${
629 // newVerificationCode.code
630 // }, to verify your account`,
631 message: template,
632 subject: `Welcome to myebucks App`
633 });
634 return {
635 user,
636 status: "successful",
637 message: "Please verify with the code sent to you"
638 };
639}
640
641export async function verifyUser(phonenumber: string, code?: number) {
642 // convert phone number to +234 format
643 if (phonenumber.startsWith("0")) {
644 let tel = phonenumber;
645 phonenumber = "+234" + tel.substr(1);
646 }
647
648 // check if an activation Code exists before for the user
649 const verificationCodes = await prisma.verificationCodes({
650 where: {
651 user: {
652 phonenumber
653 }
654 }
655 });
656
657 if (verificationCodes.length < 1) {
658 throw new Error("Error occured while trying to verify");
659 }
660
661 const verificationCode = verificationCodes[0];
662
663 if (verificationCode.code === code) {
664 const expTime = new Date(verificationCode.updatedAt);
665 let now = new Date();
666 // subtract date
667 let diffMs = now.getTime() - expTime.getTime();
668 let diffMins = Math.round(((diffMs % 86400000) % 3600000) / 60000); // minutes
669 if (diffMins <= 60) {
670 // update the user verified status to true
671 await prisma.updateUser({
672 where: {
673 phonenumber
674 },
675 data: {
676 verified: true
677 }
678 });
679 // create Wallet
680 await prisma.createWallet({
681 amount: 0,
682 user: {
683 connect: {
684 phonenumber
685 }
686 }
687 });
688
689 // delete verification code
690 await prisma.deleteVerificationCode({
691 id: verificationCode.id
692 });
693
694 const user = await prisma.user({
695 phonenumber
696 });
697
698 const template = `<html>
699
700 <head>
701 <title></title>
702 <link href='https://fonts.googleapis.com/css?family=Rubik' rel='stylesheet'>
703 <style>
704 @import url('https://fonts.googleapis.com/css?family=Rubik');
705 </style>
706 </head>
707
708 <body>
709 <div style="background-color:#f1f3f5;color:#233858;font-family:'Rubik';margin:0">
710 <div style="background-color:white;display:block;margin:0 auto;max-width:720px">
711 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
712 <tbody>
713 <tr>
714 <td
715 style="font-size:0!important;line-height:100%;padding-left:16px;padding-right:16px;padding-top:36px;text-align:center">
716
717 <div
718 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
719 <table width="100%" border="0" cellspacing="0" cellpadding="0"
720 style="min-width:100%;width:100%">
721 <tbody>
722 <tr>
723 <td
724 style="line-height:100%;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
725 <a href="#m_2661994423616798564_m_222366038327339295_"
726 style="color:#233858;font-family:'Rubik';text-decoration:underline">
727 <img src="https://img.techpowerup.org/200427/vector.png" width="64"
728 height="24" style="height:auto;max-width:200px;width:auto"
729 class="CToWUd">
730 <br />
731 <img src="https://img.techpowerup.org/200427/frame.png" width="64"
732 height="24"
733 style="height:auto;max-width:65px;width:auto;margin-top: 5px;"
734 class="CToWUd">
735 </a>
736 </td>
737 </tr>
738 </tbody>
739 </table>
740 <table align="center" width="100%" border="0" cellspacing="0" cellpadding="0"
741 style="display:table;margin-left:auto;margin-right:auto">
742 <tbody>
743 <tr>
744 <td
745 style="background-color:transparent!important;border-bottom:1px solid rgba(60,60,60,0.07);border-bottom-color:rgba(60,60,60,0.07);font-size:0;height:2px;line-height:1px;min-height:1px;overflow:hidden;padding-bottom:24px">
746 </td>
747 </tr>
748 </tbody>
749 </table>
750 </div>
751
752 </td>
753 </tr>
754 </tbody>
755 </table>
756
757 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
758 <tbody>
759 <tr>
760 <td
761 style="font-size:0!important;line-height:100%;padding-left:8px;padding-right:8px;text-align:center">
762
763 <div
764 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
765 <table width="100%" border="0" cellspacing="0" cellpadding="0"
766 style="border:2px solid;border-color:transparent;border-radius:6px;min-width:100%;width:100%">
767 <tbody>
768 <tr>
769 <td
770 style="background-position:50% 0;background-repeat:no-repeat;border:0;border-radius:3px;min-width:100%;padding-left:8px;padding-right:8px;padding-top:32px;text-align:left;vertical-align:top;width:100%">
771 <h1
772 style="color: #000000;font-family:'Rubik'!important;font-size:22px;font-weight:800;line-height:44px;margin-bottom:14px;margin-left:0;margin-right:0;margin-top:32px;padding:0">
773 Hi ${user.fullname}, </h1>
774 <p
775 style="color: #82859A;font-family:'Rubik';font-size:12px;line-height:1.6;margin-bottom:30px;margin-top:0">
776 Thank you for creating your account on myebucks. You can now pay
777 your bills and make airtime and data purchase seamleassly.</p>
778
779 <p
780 style="color: #82859A;font-family:'Rubik';font-size:13px;line-height:1.6;margin-top:0;padding-top:36px">
781 Sincerely,</p>
782 <p
783 style="color: #82859A;font-family:'Rubik';font-size:13px!important;margin-top:10px">
784 The Ebucks Team.</p>
785 </td>
786 </tr>
787 </tbody>
788 </table>
789 </div>
790
791 </td>
792 </tr>
793 </tbody>
794 </table>
795
796 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
797 <tbody>
798 <tr>
799 <td
800 style="font-size:0!important;line-height:100%;padding-left:8px;padding-right:8px;text-align:center">
801 <div
802 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
803 <table width="100%" border="0" cellspacing="0" cellpadding="0"
804 style="border:2px solid;border-color:transparent;border-radius:6px;min-width:100%;width:100%">
805 <tbody>
806 <tr>
807 <td
808 style="background-position:50% 0;background-repeat:no-repeat;border:0;border-radius:3px;min-width:100%;padding-left:8px;padding-right:8px;padding-top:32px;text-align:left;vertical-align:top;width:100%">
809 <p
810 style="border-top:1px solid rgba(60,60,60,0.07);color: #82859A;font-family:'Rubik';font-size:13px;letter-spacing: 0.02em;line-height:1.6;margin-bottom:20px;margin-top:100px;padding-top:20px">
811 Fund Wallet & Purchase with ease with our app today, availabe on
812 <a href="#">Android</a> and <a href="#">iOS</a> (Coming Soon) </p>
813 </td>
814 </tr>
815 </tbody>
816 </table>
817 </div>
818
819 </td>
820 </tr>
821 </tbody>
822 </table>
823
824
825
826 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
827 <tbody>
828 <tr>
829 <td
830 style="background-color:#0041C4;display:block;font-size:0!important;height:270px;line-height:100%;margin-top:20px;padding-left:8px;padding-right:8px;text-align:center">
831
832 <div
833 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
834 <table width="100%" border="0" cellspacing="0" cellpadding="0"
835 style="min-width:100%;width:100%">
836 <tbody>
837 <tr>
838 <td
839 style="min-width:100%;padding-left:8px;padding-right:8px;vertical-align:top;width:100%">
840 <div
841 style="display:inline-block;float:left;font-size:0;min-width:0!important;vertical-align:top;width:25%">
842 <table width="100%" border="0" cellspacing="0" cellpadding="0"
843 style="min-width:100%;padding-top:32px;width:100%">
844 <tbody>
845 <tr>
846 <td
847 style="color:#233858;font-size:16px;line-height:100%;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
848 <a href="#"
849 style="color:#233858;font-family:'Rubik';text-decoration:underline">
850 <img src="https://img.techpowerup.org/200427/frame-1.png"
851 width="64" height="24"
852 style="height:24px;max-width:200px;width:auto;"
853 class="CToWUd"> <br />
854 <img src="https://img.techpowerup.org/200427/frame-2.png"
855 width="64" height="24"
856 style="height:auto;max-width:65px;width:auto;margin-top: 5px;"
857 class="CToWUd">
858 </a>
859 </td>
860 </tr>
861 </tbody>
862 </table>
863 </div>
864 <div
865 style="display:inline-block;float:right;font-size:0;min-width:0!important;vertical-align:top;width:25%">
866 <table width="100%" border="0" cellspacing="0" cellpadding="0"
867 style="margin-left:auto;margin-right:0;min-width:100%;padding-top:32px;text-align:right;width:100%">
868 <tbody>
869 <tr>
870 <td style="color:#233858;display:inline;font-size:16px;min-width:100%;padding-left:8px;padding-right:8px;vertical-align:top;width:100%; display: flex;
871 flex-direction: column;align-items: center;">
872 <a href="#"
873 style="color:#233858;font-family:'Rubik';text-decoration:underline; margin-top: 15px;"
874 target="_blank" data-saferedirecturl="#"><img
875 src="https://ci6.googleusercontent.com/proxy/QibhghTDk6b0hIfNEbXhPkXgN1dvldl09KWTEGXqVOqUVuIUMbye4bx_nb1fo3kXVlLdOqShZs8peXzMvw=s0-d-e1-ft#https://emery.cloudns.cx/static/img/tw.png"
876 alt="myebucks Twitter"
877 style="height:16px;margin-left:20px;max-width:200px;width:auto"
878 class="CToWUd"></a>
879 <a href="#"
880 style="color:#233858;font-family:'Rubik';text-decoration:underline; margin-top: 15px;"
881 target="_blank" data-saferedirecturl="#"><img
882 src="https://ci5.googleusercontent.com/proxy/D1G7umjs3HsEa9BlJ6D8ZYRJYHUIrFdZrWaLNQiUfInBYUQmHzcjoEsaCqSoyzFUzEQ6F8n2lT0N_2Mf2A=s0-d-e1-ft#https://emery.cloudns.cx/static/img/ig.png"
883 alt="myebucks Instagram"
884 style="height:16px;margin-left:20px;max-width:200px;width:auto"
885 class="CToWUd"></a>
886 <a href="#"
887 style="color:#233858;font-family:'Rubik';text-decoration:underline; margin-top: 15px;"
888 target="_blank" data-saferedirecturl="#"><img
889 src="https://ci3.googleusercontent.com/proxy/aVFBIC7qxNzk6PsoiyvWZjHrD6VrlqJ_aUP_yC3eiSLDZmAf2ID-qBV9sJDs7UUlwcgdEfkOCYFg15JaYA=s0-d-e1-ft#https://emery.cloudns.cx/static/img/fb.png"
890 alt="myebucks Facebook"
891 style="height:16px;margin-left:20px;max-width:200px;width:auto"
892 class="CToWUd"></a>
893 </td>
894 </tr>
895 </tbody>
896 </table>
897 </div>
898
899 <div
900 style="display:inline-block;float:left;font-size:0;min-width:0!important;vertical-align:top;width:25%">
901 <table width="100%" border="0" cellspacing="0" cellpadding="0"
902 style="min-width:100%;padding-top:32px;width:100%">
903 <tbody>
904 <tr>
905 <td
906 style="color:#233858;font-size:16px;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
907 <a href=""
908 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
909 target="_blank">Links</a>
910 <a href=""
911 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
912 target="_blank">About Us</a>
913 <a href=""
914 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
915 target="_blank">Careers</a>
916 <a href=""
917 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
918 target="_blank">Contact</a>
919 </td>
920 </tr>
921 </tbody>
922 </table>
923 </div>
924 <div
925 style="display:inline-block;float:right;font-size:0;min-width:0!important;vertical-align:top;width:25%">
926 <table width="100%" border="0" cellspacing="0" cellpadding="0"
927 style="min-width:100%;padding-top:32px;width:100%">
928 <tbody>
929 <tr>
930 <td
931 style="color:#233858;font-size:16px;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
932 <p
933 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
934 Office</p>
935 <p
936 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
937 23 Adekunle Akala Street,</p>
938 <p
939 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
940 Ajao Estate, Oshodi/Isolo.</p>
941 <p
942 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
943 Lagos State</p>
944 </td>
945 </tr>
946 </tbody>
947 </table>
948 </div>
949
950
951
952 <table align="center" width="100%" border="0" cellspacing="0"
953 cellpadding="0"
954 style="display:table;margin-left:auto;margin-right:auto;padding-top:32px">
955 <tbody>
956 <tr>
957 <td
958 style="background-color:transparent!important;border-bottom:1px solid rgba(255,255,255,0.07);color:#233858;font-size:16px;height:2px;line-height:1px;min-height:1px;overflow:hidden">
959 </td>
960 </tr>
961 </tbody>
962 </table>
963 </td>
964 </tr>
965 </tbody>
966 </table>
967 </div>
968
969 </td>
970 </tr>
971 </tbody>
972 </table>
973
974 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width: 100%; width: 100%;">
975 <tbody>
976 <tr>
977 <td
978 style="display: block; font-size: 0 !important;height: 20px;margin-top: 20px;padding-left: 8px;padding-right: 8px;">
979 <div
980 style="display:inline-block;float:left;font-size:0;min-width:0!important;vertical-align:top;">
981 <table width="100%" border="0" cellspacing="0" cellpadding="0"
982 style="min-width:100%;width:100%">
983 <tbody>
984 <tr>
985 <td>
986 <p style="color: #595959;font-family: Rubik;
987 font-style: normal;
988 font-weight: bold;
989 font-size: 12px;
990 line-height: 12px;">Powered By <span><img width="64"
991 height="24" style="height:auto;width:auto;"
992 src="https://img.techpowerup.org/200503/group-135.png" /></span>
993 </p>
994 </td>
995 </tr>
996 </tbody>
997 </table>
998 </div>
999 </td>
1000 </tr>
1001 </tbody>
1002 </table>
1003 <div class="yj6qo"></div>
1004 <div class="adL">
1005 </div>
1006 </div>
1007 <div class="adL">
1008 </div>
1009 </div>
1010 </body>
1011
1012 </html>`;
1013
1014 mail({
1015 user,
1016 // message: `Hello ${user.fullname}. Welcome to Payment App. Enter this ${
1017 // newVerificationCode.code
1018 // }, to verify your account`,
1019 message: template,
1020 subject: `Welcome to Payment App`
1021 });
1022
1023
1024 return {
1025 user,
1026 status: "successful",
1027 token: sign(
1028 {
1029 id: user.id,
1030 email: user.email,
1031 user: "user"
1032 },
1033 process.env.JWT_SECRET
1034 )
1035 };
1036 }
1037 throw new Error("Verification code expired");
1038 }
1039 throw new Error("Incorrect verification code");
1040}
1041
1042interface LoginData {
1043 phonenumber: string;
1044 password: string;
1045}
1046
1047export async function login(loginData: LoginData) {
1048 let { phonenumber, password } = loginData;
1049 // convert phone number to +234 format
1050 if (phonenumber.startsWith("0")) {
1051 let tel = phonenumber;
1052 phonenumber = "+234" + tel.substr(1);
1053 }
1054 const user = await prisma.user({
1055 phonenumber
1056 });
1057 // if no user
1058 if (!user) {
1059 throw new Error("Invalid Details");
1060 }
1061 // Confirm Password
1062 const valid = await bcrypt.compare(password, user.password);
1063 if (!valid) {
1064 throw new Error("Invalid Details");
1065 }
1066
1067 const retrieve = await retrieveVirtualAccount(user.trackingReference);
1068 // let accountNo = retrieve,d
1069 // await prisma.createKudaDetail(retrieve.data)
1070
1071 // return json web token
1072 return {
1073 retrieve,
1074 user,
1075 token: sign(
1076 {
1077 id: user.id,
1078 email: user.email,
1079 phonenumber: user.phonenumber,
1080 user: "user"
1081 },
1082 process.env.JWT_SECRET
1083 )
1084 };
1085}
1086
1087export async function sendPasswordResetCode(
1088 phonenumber: string,
1089 code?: number
1090) {
1091 // convert phone number to +234 format
1092 if (phonenumber.startsWith("0")) {
1093 let tel = phonenumber;
1094 phonenumber = "+234" + tel.substr(1);
1095 }
1096 if (!code) {
1097 code = Math.floor(Math.random() * 900000) + 100000;
1098 }
1099
1100 if (String(code).length !== 6) {
1101 throw new Error("Code must be 6 digits");
1102 }
1103
1104 // fetch user
1105 const user = await prisma.user({
1106 phonenumber
1107 });
1108
1109 // password reset codes
1110 const passwordResetCodes = await prisma.passwordResetCodes({
1111 where: {
1112 user: {
1113 phonenumber
1114 }
1115 }
1116 });
1117
1118 // check if reset code exist before
1119 if (passwordResetCodes.length > 0) {
1120 // password reset code
1121 const passwordResetCode = passwordResetCodes[0];
1122
1123 await prisma.deletePasswordResetCode({
1124 id: passwordResetCode.id
1125 });
1126 }
1127
1128 await prisma.createPasswordResetCode({
1129 code,
1130 user: {
1131 connect: {
1132 phonenumber
1133 }
1134 }
1135 });
1136
1137 // Mask email address
1138 const maskEmail = user.email.replace(/^(.)(.*)(.@.*)$/,
1139 (_, a, b, c) => a + b.replace(/./g, '*') + c
1140 );
1141
1142 const d = new Date();
1143 const today = ((d.getMonth() + 1) + '/' + d.getDate() + '/' + d.getFullYear());
1144 const time = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();
1145
1146 const template = `
1147 <html>
1148
1149<head>
1150 <title></title>
1151 <link href='https://fonts.googleapis.com/css?family=Rubik' rel='stylesheet'>
1152 <style>
1153 @import url('https://fonts.googleapis.com/css?family=Rubik');
1154 </style>
1155</head>
1156
1157<body>
1158 <div style="background-color:#f1f3f5;color:#233858;font-family:'Rubik';margin:0">
1159 <div style="background-color:white;display:block;margin:0 auto;max-width:720px">
1160 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
1161 <tbody>
1162 <tr>
1163 <td
1164 style="font-size:0!important;line-height:100%;padding-left:16px;padding-right:16px;padding-top:36px;text-align:center">
1165
1166 <div
1167 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
1168 <table width="100%" border="0" cellspacing="0" cellpadding="0"
1169 style="min-width:100%;width:100%">
1170 <tbody>
1171 <tr>
1172 <td
1173 style="line-height:100%;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
1174 <a href="#m_2661994423616798564_m_222366038327339295_"
1175 style="color:#233858;font-family:'Rubik';text-decoration:underline">
1176 <img src="https://img.techpowerup.org/200427/vector.png" width="64"
1177 height="24" style="height:auto;max-width:200px;width:auto"
1178 class="CToWUd">
1179 <br />
1180 <img src="https://img.techpowerup.org/200427/frame.png" width="64"
1181 height="24"
1182 style="height:auto;max-width:65px;width:auto;margin-top: 5px;"
1183 class="CToWUd">
1184 </a>
1185 </td>
1186 </tr>
1187 </tbody>
1188 </table>
1189 <table align="center" width="100%" border="0" cellspacing="0" cellpadding="0"
1190 style="display:table;margin-left:auto;margin-right:auto">
1191 <tbody>
1192 <tr>
1193 <td
1194 style="background-color:transparent!important;border-bottom:1px solid rgba(60,60,60,0.07);border-bottom-color:rgba(60,60,60,0.07);font-size:0;height:2px;line-height:1px;min-height:1px;overflow:hidden;padding-bottom:24px">
1195 </td>
1196 </tr>
1197 </tbody>
1198 </table>
1199 </div>
1200
1201 </td>
1202 </tr>
1203 </tbody>
1204 </table>
1205
1206 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
1207 <tbody>
1208 <tr>
1209 <td
1210 style="font-size:0!important;line-height:100%;padding-left:8px;padding-right:8px;text-align:center">
1211
1212 <div
1213 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
1214 <table width="100%" border="0" cellspacing="0" cellpadding="0"
1215 style="border:2px solid;border-color:transparent;border-radius:6px;min-width:100%;width:100%">
1216 <tbody>
1217 <tr>
1218 <td
1219 style="background-position:50% 0;background-repeat:no-repeat;border:0;border-radius:3px;min-width:100%;padding-left:8px;padding-right:8px;padding-top:32px;text-align:left;vertical-align:top;width:100%">
1220 <h1
1221 style="color: #000000;font-family:'Rubik'!important;font-size:22px;font-weight:800;line-height:44px;margin-bottom:14px;margin-left:0;margin-right:0;margin-top:32px;padding:0">
1222 No Hassle, its alright to forget</h1>
1223 <p
1224 style="color: #82859A;font-family:'Rubik';font-size:12px;line-height:1.6;margin-bottom:30px;margin-top:0;">
1225 Hi ${user.fullname}, you asked to reset your password for your
1226 myebucks acccount <span
1227 style="color: #044CC5;">${maskEmail}</span> on ${today} ${time}
1228 12:20 AM(WAT)</p>
1229 <p
1230 style="color: #82859A;font-family:'Rubik';font-size:12px;line-height:1.6;margin-bottom:30px;margin-top:0;">
1231 Use this code <strong>098764</strong></p>
1232
1233 <p
1234 style="color: #82859A;font-family:'Rubik';font-size:13px;line-height:1.6;margin-top:0;padding-top:36px">
1235 Sincerely,</p>
1236 <p
1237 style="color: #82859A;font-family:'Rubik';font-size:13px!important;margin-top:10px">
1238 The Ebucks Team.</p>
1239 </td>
1240 </tr>
1241 </tbody>
1242 </table>
1243 </div>
1244
1245 </td>
1246 </tr>
1247 </tbody>
1248 </table>
1249
1250 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
1251 <tbody>
1252 <tr>
1253 <td
1254 style="font-size:0!important;line-height:100%;padding-left:8px;padding-right:8px;text-align:center">
1255 <div
1256 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
1257 <table width="100%" border="0" cellspacing="0" cellpadding="0"
1258 style="border:2px solid;border-color:transparent;border-radius:6px;min-width:100%;width:100%">
1259 <tbody>
1260 <tr>
1261 <td
1262 style="background-position:50% 0;background-repeat:no-repeat;border:0;border-radius:3px;min-width:100%;padding-left:8px;padding-right:8px;padding-top:32px;text-align:left;vertical-align:top;width:100%">
1263 <p
1264 style="border-top:1px solid rgba(60,60,60,0.07);color: #82859A;font-family:'Rubik';font-size:13px;letter-spacing: 0.02em;line-height:1.6;margin-bottom:20px;margin-top:100px;padding-top:20px">
1265 Fund Wallet & Purchase with ease with our app today, availabe on
1266 <a href="#">Android</a> and <a href="#">iOS</a> (Coming Soon) </p>
1267 </td>
1268 </tr>
1269 </tbody>
1270 </table>
1271 </div>
1272
1273 </td>
1274 </tr>
1275 </tbody>
1276 </table>
1277
1278
1279
1280 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width:100%;width:100%">
1281 <tbody>
1282 <tr>
1283 <td
1284 style="background-color:#0041C4;display:block;font-size:0!important;height:270px;line-height:100%;margin-top:20px;padding-left:8px;padding-right:8px;text-align:center">
1285
1286 <div
1287 style="display:inline-block;font-size:0;margin:0 auto;max-width:660px;min-width:0!important;text-align:center;vertical-align:top;width:100%">
1288 <table width="100%" border="0" cellspacing="0" cellpadding="0"
1289 style="min-width:100%;width:100%">
1290 <tbody>
1291 <tr>
1292 <td
1293 style="min-width:100%;padding-left:8px;padding-right:8px;vertical-align:top;width:100%">
1294 <div
1295 style="display:inline-block;float:left;font-size:0;min-width:0!important;vertical-align:top;width:25%">
1296 <table width="100%" border="0" cellspacing="0" cellpadding="0"
1297 style="min-width:100%;padding-top:32px;width:100%">
1298 <tbody>
1299 <tr>
1300 <td
1301 style="color:#233858;font-size:16px;line-height:100%;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
1302 <a href="#"
1303 style="color:#233858;font-family:'Rubik';text-decoration:underline">
1304 <img src="https://img.techpowerup.org/200427/frame-1.png"
1305 width="64" height="24"
1306 style="height:24px;max-width:200px;width:auto;"
1307 class="CToWUd"> <br />
1308 <img src="https://img.techpowerup.org/200427/frame-2.png"
1309 width="64" height="24"
1310 style="height:auto;max-width:65px;width:auto;margin-top: 5px;"
1311 class="CToWUd">
1312 </a>
1313 </td>
1314 </tr>
1315 </tbody>
1316 </table>
1317 </div>
1318 <div
1319 style="display:inline-block;float:right;font-size:0;min-width:0!important;vertical-align:top;width:25%">
1320 <table width="100%" border="0" cellspacing="0" cellpadding="0"
1321 style="margin-left:auto;margin-right:0;min-width:100%;padding-top:32px;text-align:right;width:100%">
1322 <tbody>
1323 <tr>
1324 <td style="color:#233858;display:inline;font-size:16px;min-width:100%;padding-left:8px;padding-right:8px;vertical-align:top;width:100%; display: flex;
1325 flex-direction: column;align-items: center;">
1326 <a href="#"
1327 style="color:#233858;font-family:'Rubik';text-decoration:underline; margin-top: 15px;"
1328 target="_blank" data-saferedirecturl="#"><img
1329 src="https://ci6.googleusercontent.com/proxy/QibhghTDk6b0hIfNEbXhPkXgN1dvldl09KWTEGXqVOqUVuIUMbye4bx_nb1fo3kXVlLdOqShZs8peXzMvw=s0-d-e1-ft#https://emery.cloudns.cx/static/img/tw.png"
1330 alt="myebucks Twitter"
1331 style="height:16px;margin-left:20px;max-width:200px;width:auto"
1332 class="CToWUd"></a>
1333 <a href="#"
1334 style="color:#233858;font-family:'Rubik';text-decoration:underline; margin-top: 15px;"
1335 target="_blank" data-saferedirecturl="#"><img
1336 src="https://ci5.googleusercontent.com/proxy/D1G7umjs3HsEa9BlJ6D8ZYRJYHUIrFdZrWaLNQiUfInBYUQmHzcjoEsaCqSoyzFUzEQ6F8n2lT0N_2Mf2A=s0-d-e1-ft#https://emery.cloudns.cx/static/img/ig.png"
1337 alt="myebucks Instagram"
1338 style="height:16px;margin-left:20px;max-width:200px;width:auto"
1339 class="CToWUd"></a>
1340 <a href="#"
1341 style="color:#233858;font-family:'Rubik';text-decoration:underline; margin-top: 15px;"
1342 target="_blank" data-saferedirecturl="#"><img
1343 src="https://ci3.googleusercontent.com/proxy/aVFBIC7qxNzk6PsoiyvWZjHrD6VrlqJ_aUP_yC3eiSLDZmAf2ID-qBV9sJDs7UUlwcgdEfkOCYFg15JaYA=s0-d-e1-ft#https://emery.cloudns.cx/static/img/fb.png"
1344 alt="myebucks Facebook"
1345 style="height:16px;margin-left:20px;max-width:200px;width:auto"
1346 class="CToWUd"></a>
1347 </td>
1348 </tr>
1349 </tbody>
1350 </table>
1351 </div>
1352
1353 <div
1354 style="display:inline-block;float:left;font-size:0;min-width:0!important;vertical-align:top;width:25%">
1355 <table width="100%" border="0" cellspacing="0" cellpadding="0"
1356 style="min-width:100%;padding-top:32px;width:100%">
1357 <tbody>
1358 <tr>
1359 <td
1360 style="color:#233858;font-size:16px;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
1361 <a href=""
1362 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
1363 target="_blank">Links</a>
1364 <a href=""
1365 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
1366 target="_blank">About Us</a>
1367 <a href=""
1368 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
1369 target="_blank">Careers</a>
1370 <a href=""
1371 style="color:white;display:block;font-family:'Rubik';font-size:13px;margin-bottom:15px;text-decoration:none!important"
1372 target="_blank">Contact</a>
1373 </td>
1374 </tr>
1375 </tbody>
1376 </table>
1377 </div>
1378 <div
1379 style="display:inline-block;float:right;font-size:0;min-width:0!important;vertical-align:top;width:25%">
1380 <table width="100%" border="0" cellspacing="0" cellpadding="0"
1381 style="min-width:100%;padding-top:32px;width:100%">
1382 <tbody>
1383 <tr>
1384 <td
1385 style="color:#233858;font-size:16px;min-width:100%;padding-left:8px;padding-right:8px;text-align:left;vertical-align:top;width:100%">
1386 <p
1387 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
1388 Office</p>
1389 <p
1390 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
1391 23 Adekunle Akala Street,</p>
1392 <p
1393 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
1394 Ajao Estate, Oshodi/Isolo.</p>
1395 <p
1396 style="color:white;display:block;font-family:'Rubik';font-size:13px;line-height:1.6;margin-bottom:10px;margin-top:0;">
1397 Lagos State</p>
1398 </td>
1399 </tr>
1400 </tbody>
1401 </table>
1402 </div>
1403
1404
1405
1406 <table align="center" width="100%" border="0" cellspacing="0"
1407 cellpadding="0"
1408 style="display:table;margin-left:auto;margin-right:auto;padding-top:32px">
1409 <tbody>
1410 <tr>
1411 <td
1412 style="background-color:transparent!important;border-bottom:1px solid rgba(255,255,255,0.07);color:#233858;font-size:16px;height:2px;line-height:1px;min-height:1px;overflow:hidden">
1413 </td>
1414 </tr>
1415 </tbody>
1416 </table>
1417 </td>
1418 </tr>
1419 </tbody>
1420 </table>
1421 </div>
1422
1423 </td>
1424 </tr>
1425 </tbody>
1426 </table>
1427
1428 <table width="100%" border="0" cellspacing="0" cellpadding="0" style="min-width: 100%; width: 100%;">
1429 <tbody>
1430 <tr>
1431 <td
1432 style="display: block; font-size: 0 !important;height: 20px;margin-top: 20px;padding-left: 8px;padding-right: 8px;">
1433 <div
1434 style="display:inline-block;float:left;font-size:0;min-width:0!important;vertical-align:top;">
1435 <table width="100%" border="0" cellspacing="0" cellpadding="0"
1436 style="min-width:100%;width:100%">
1437 <tbody>
1438 <tr>
1439 <td>
1440 <p style="color: #595959;font-family: Rubik;
1441 font-style: normal;
1442 font-weight: bold;
1443 font-size: 12px;
1444 line-height: 12px;">Powered By <span><img width="64"
1445 height="24" style="height:auto;width:auto;"
1446 src="https://img.techpowerup.org/200503/group-135.png" /></span>
1447 </p>
1448 </td>
1449 </tr>
1450 </tbody>
1451 </table>
1452 </div>
1453 </td>
1454 </tr>
1455 </tbody>
1456 </table>
1457 <div class="yj6qo"></div>
1458 <div class="adL">
1459 </div>
1460 </div>
1461 <div class="adL">
1462 </div>
1463 </div>
1464</body>
1465
1466</html>`;
1467
1468 try {
1469 await mail({
1470 user,
1471 // message: `Hi ${
1472 // user.fullname
1473 // }. Use this code: ${code} to reset your password`,
1474 message: template,
1475 subject: "myebucks App Password Reset"
1476 });
1477
1478 return {
1479 user,
1480 status: "successful",
1481 message: "Please reset your password with the code sent to you"
1482 };
1483 } catch (error) { }
1484}
1485
1486export async function resetPassword(
1487 phonenumber: string,
1488 code: number,
1489 password: string
1490) {
1491 // convert phone number to +234 format
1492 if (phonenumber.startsWith("0")) {
1493 let tel = phonenumber;
1494 phonenumber = "+234" + tel.substr(1);
1495 }
1496
1497 const passwordResetCodes = await prisma.passwordResetCodes({
1498 where: {
1499 user: {
1500 phonenumber
1501 }
1502 }
1503 });
1504
1505 if (passwordResetCodes.length === 0) {
1506 throw new Error("Request for a password reset");
1507 }
1508
1509 const passwordResetCode = passwordResetCodes[0];
1510
1511 // if code align change the password
1512 if (passwordResetCode.code === code) {
1513 const expTime = new Date(passwordResetCode.updatedAt);
1514 let now = new Date();
1515 // subtract date
1516 let diffMs = now.getTime() - expTime.getTime();
1517 let diffMins = Math.round(((diffMs % 86400000) % 3600000) / 60000); // minutes
1518
1519 if (diffMins <= 60) {
1520 const user = await prisma.updateUser({
1521 where: {
1522 phonenumber
1523 },
1524 data: {
1525 password: await bcrypt.hashSync(password, salt)
1526 }
1527 });
1528
1529 return {
1530 user,
1531 token: sign(
1532 {
1533 id: user.id,
1534 email: user.email,
1535 user: "user"
1536 },
1537 process.env.JWT_SECRET
1538 )
1539 };
1540 }
1541 throw new Error("Password reset code expired");
1542 }
1543 throw new Error("Wrong password reset code");
1544}
1545
1546export async function user(token?: string, phonenumber?: string) {
1547 if (token) {
1548 const { id, user } = verifyToken(token) as any;
1549 if (user === "user") {
1550 return prisma.user({
1551 id
1552 });
1553 }
1554 throw new Error("Not Authorized");
1555 } else if (phonenumber) {
1556 if (phonenumber.length < 11) {
1557 throw new Error("Your phone number is incomplete");
1558 }
1559 // convert phone number to +234 format
1560 if (phonenumber.startsWith("0")) {
1561 let tel = phonenumber;
1562 phonenumber = "+234" + tel.substr(1);
1563 }
1564 return prisma.user({
1565 phonenumber
1566 });
1567 }
1568 throw new Error("Not Authorized");
1569}
1570
1571export async function updatePassword(
1572 token: string,
1573 oldPassword: string,
1574 newPassword: string
1575) {
1576 const { id, email, user } = verifyToken(token) as any;
1577
1578 // checks if the type of user is a user
1579 if (user === "user") {
1580 const person = await prisma.user({
1581 email
1582 });
1583 // Confirm Old Password
1584 const valid = await bcrypt.compare(oldPassword, person.password);
1585 if (!valid) {
1586 throw new Error("Old Password Incorrect");
1587 }
1588
1589 // confirm user is not using the same password
1590 const newValid = await bcrypt.compare(newPassword, person.password);
1591 if (newValid) {
1592 throw new Error("Choose a new password");
1593 }
1594
1595 await prisma.updateUser({
1596 where: {
1597 email
1598 },
1599 data: {
1600 password: await bcrypt.hashSync(newPassword, salt)
1601 }
1602 });
1603
1604 return {
1605 user: person,
1606 token: sign(
1607 {
1608 id: user.id,
1609 email: user.email,
1610 user: "user"
1611 },
1612 process.env.JWT_SECRET
1613 )
1614 };
1615 }
1616 throw new Error("Not Authorized");
1617}
1618
1619export async function updateProfile(
1620 token: string,
1621 fullname: string,
1622 gender: Gender
1623) {
1624 const { id, email, user } = verifyToken(token) as any;
1625 console.log(fullname);
1626
1627 // checks if the type of user is a user
1628 if (user === "user") {
1629 const person = await prisma.user({
1630 email
1631 });
1632
1633 const newUser = await prisma.updateUser({
1634 where: {
1635 email
1636 },
1637 data: {
1638 fullname,
1639 gender
1640 }
1641 });
1642
1643 return {
1644 user: newUser,
1645 status: "successful",
1646 message: "Your profile has been updated"
1647 };
1648 }
1649 throw new Error("Not Authorized");
1650}