├── .gitignore ├── bin └── dev ├── views ├── partials │ ├── footer.ejs │ └── header.ejs ├── money │ ├── addFunds.ejs │ ├── request.ejs │ ├── cashout.ejs │ ├── transfer.ejs │ └── dashboard.ejs ├── users │ ├── login.ejs │ └── signup.ejs └── home.ejs ├── public ├── images │ ├── bag-of-money.jpg │ └── jumbotron-image.jpg └── stylesheets │ └── app.css ├── middlewares └── index.js ├── db.js ├── package.json ├── email └── nodemailer.js ├── routes ├── users.js └── money.js ├── seed.sql ├── app.js └── controllers ├── userController.js └── moneyController.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | .prettierignore 3 | .env 4 | .seed -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | require('./../app.js') -------------------------------------------------------------------------------- /views/partials/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/bag-of-money.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viniciuscsr/cashapp/HEAD/public/images/bag-of-money.jpg -------------------------------------------------------------------------------- /public/images/jumbotron-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viniciuscsr/cashapp/HEAD/public/images/jumbotron-image.jpg -------------------------------------------------------------------------------- /middlewares/index.js: -------------------------------------------------------------------------------- 1 | function isLoggedIn(req, res, next) { 2 | if (!req.user) { 3 | return res.redirect('/users/login'); 4 | } 5 | next(); 6 | } 7 | 8 | module.exports = isLoggedIn; 9 | -------------------------------------------------------------------------------- /db.js: -------------------------------------------------------------------------------- 1 | const Pool = require('pg').Pool; 2 | 3 | const pool = new Pool({ 4 | connectionString: 5 | process.env.DATABASE_URL || 6 | 'postgresql://node_user:node_password@localhost:5432/cashapp', 7 | ssl: false, 8 | }); 9 | 10 | module.exports = pool; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cashapp", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node app.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^4.0.1", 14 | "body-parser": "^1.19.0", 15 | "client-sessions": "^0.8.0", 16 | "connect-flash": "^0.1.1", 17 | "cookie-parser": "^1.4.5", 18 | "csurf": "^1.11.0", 19 | "ejs": "^3.1.3", 20 | "express": "^4.17.1", 21 | "express-session": "^1.17.1", 22 | "express-validator": "^6.5.0", 23 | "nodemailer": "^6.4.8", 24 | "nodemon": "^2.0.4", 25 | "pg": "^8.2.1" 26 | }, 27 | "devDependencies": { 28 | "dotenv": "^8.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /email/nodemailer.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require('nodemailer'); 2 | 3 | async function requestMoneyEmail( 4 | requestee_name, 5 | requestee_email, 6 | requestor_name, 7 | amount 8 | ) { 9 | let transporter = nodemailer.createTransport({ 10 | host: 'smtp.mailtrap.io', 11 | port: 2525, 12 | auth: { 13 | user: '95a32120ba1fac', 14 | pass: 'bb4e04b89a0d33', 15 | }, 16 | }); 17 | 18 | mailOptions = { 19 | from: '"CashApp Team" ', 20 | to: `${requestee_email}`, 21 | subject: `You Received a Money Request from ${requestor_name}`, 22 | text: `Hi ${requestee_name} , you received a money request of USD ${amount} from ${requestor_name}`, 23 | html: `Hi ${requestee_name}!
, you received a money request of ${amount} USD from ${requestor_name}`, 24 | }; 25 | 26 | let info; 27 | try { 28 | info = await transporter.sendMail(mailOptions); 29 | } catch (err) { 30 | console.log(err); 31 | } 32 | console.log('Message sent: %s', info.messageId); 33 | } 34 | 35 | module.exports = requestMoneyEmail; 36 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const { check } = require('express-validator'); 4 | const cookieParser = require('cookie-parser'); 5 | const csrf = require('csurf'); 6 | const csrfProtection = csrf({ cookie: true }); 7 | const userController = require('../controllers/userController'); 8 | 9 | router.use(cookieParser()); 10 | 11 | //-------------------- 12 | // SIGNUP 13 | //-------------------- 14 | 15 | router.get('/signup', userController.getSignup); 16 | 17 | router.post( 18 | '/signup', 19 | check('email').isEmail(), 20 | check('password').isLength({ min: 6 }), 21 | check('name').notEmpty(), 22 | userController.postSignup 23 | ); 24 | 25 | //-------------------- 26 | // LOGIN 27 | //-------------------- 28 | 29 | router.get('/login', csrfProtection, userController.getLogin); 30 | 31 | router.post('/login', userController.postLogin); 32 | 33 | //-------------------- 34 | // LOGOUT 35 | //-------------------- 36 | 37 | router.get('/logout', userController.logout); 38 | 39 | module.exports = router; 40 | -------------------------------------------------------------------------------- /seed.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE users( 2 | id SERIAL PRIMARY KEY, 3 | name VARCHAR(50), 4 | email VARCHAR(50), 5 | password VARCHAR(200) 6 | ); 7 | 8 | INSERT INTO users(name, email, password) 9 | VALUES 10 | ('user1', 'user1@user.com', 123456); 11 | 12 | CREATE TABLE transactions( 13 | id SERIAL PRIMARY KEY, 14 | date DATE NOT NULL DEFAULT CURRENT_DATE, 15 | sender_id INT NOT NULL, 16 | recipient_id INT NOT NULL, 17 | amount FLOAT(2) NOT NULL 18 | ); 19 | 20 | CREATE TABLE balance( 21 | user_id INT PRIMARY KEY, 22 | balance INT NOT NULL 23 | ); 24 | 25 | CREATE TABLE add_funds( 26 | id SERIAL PRIMARY KEY, 27 | date DATE NOT NULL DEFAULT CURRENT_DATE, 28 | user_id INT NOT NULL, 29 | bank INT NOT NULL, 30 | amount INT NOT NULL 31 | ); 32 | 33 | CREATE TABLE cash_out( 34 | id SERIAL PRIMARY KEY, 35 | date DATE NOT NULL DEFAULT CURRENT_DATE, 36 | user_id INT NOT NULL, 37 | bank VARCHAR(50) NOT NULL, 38 | amount INT NOT NULL 39 | ); 40 | 41 | INSERT INTO transactions(sender_id, recipient_id, amount) 42 | VALUES 43 | ( 3, 2, 300.00); 44 | 45 | -------------------------------------------------------------------------------- /views/money/addFunds.ejs: -------------------------------------------------------------------------------- 1 | <%- include("../partials/header") %> 2 | 3 |
4 |
5 |

6 |
7 | Add Funds to Your Account 8 |
9 |

10 |
11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 | 25 |
26 |
27 | 30 |
31 | 32 |
33 |
34 |
35 |
36 | 37 | <%- include("../partials/footer") %> 38 | -------------------------------------------------------------------------------- /views/partials/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CashApp 6 | 11 | 12 | 13 | 14 | 15 |
16 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /views/money/request.ejs: -------------------------------------------------------------------------------- 1 | <%- include("../partials/header") %> <% if(error_message){ %> 2 |
3 | 4 |
5 | <%= error_message %> 6 |
7 |
8 | <% } %> 9 | 10 |
11 |
12 |

13 |
14 | Request Money 15 |
16 |

17 |
18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 | 32 |
33 | 34 |
35 |
36 |
37 |
38 | 39 | <%- include("../partials/footer") %> 40 | -------------------------------------------------------------------------------- /views/money/cashout.ejs: -------------------------------------------------------------------------------- 1 | <%- include("../partials/header") %> <% if(error_message){ %> 2 |
3 | 4 |
5 | <%= error_message %> 6 |
7 |
8 | <% } %> 9 | 10 |
11 |
12 |

13 |
14 | Withdraw Your Funds 15 |
16 |

17 |
18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 | 32 |
33 | 34 |
35 |
36 |
37 |
38 | 39 | <%- include("../partials/footer") %> 40 | -------------------------------------------------------------------------------- /views/money/transfer.ejs: -------------------------------------------------------------------------------- 1 | <%- include("../partials/header") %> <% if(error_message){ %> 2 |
3 | 4 |
5 | <%= error_message %> 6 |
7 |
8 | <% } %> 9 | 10 |
11 |
12 |

13 |
14 | Transfer Money 15 |
16 |

17 |
18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 | 32 |
33 | 34 |
35 |
36 |
37 |
38 | 39 | <%- include("../partials/footer") %> 40 | -------------------------------------------------------------------------------- /routes/money.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const isLoggedIn = require('../middlewares/index'); 4 | const moneyController = require('../controllers/moneyController'); 5 | 6 | // ----------------- 7 | // DASHBOARD 8 | // ----------------- 9 | 10 | router.get('/', isLoggedIn, moneyController.dashboard); 11 | 12 | // ----------------- 13 | // TRANSFER MONEY TO A USER 14 | // ----------------- 15 | 16 | router.get('/transfer/new', isLoggedIn, moneyController.getTransfer); 17 | 18 | router.post('/transfer', isLoggedIn, moneyController.postTransfer); 19 | 20 | // ----------------- 21 | // REQUEST MONEY FROM A USER 22 | // ----------------- 23 | 24 | router.get('/request/new', isLoggedIn, moneyController.getRequestMoney); 25 | 26 | router.post('/request', isLoggedIn, moneyController.postRequestMoney); 27 | 28 | // ----------------- 29 | // ADD FUNDS 30 | // ----------------- 31 | 32 | router.get('/add-funds/new', isLoggedIn, moneyController.getAddFunds); 33 | 34 | router.post('/add-funds', isLoggedIn, moneyController.postAddFunds); 35 | 36 | // ----------------- 37 | // CASH OUT 38 | // ----------------- 39 | 40 | router.get('/cashout/new', isLoggedIn, moneyController.getCashout); 41 | 42 | router.post('/cashout', isLoggedIn, moneyController.postCashout); 43 | 44 | module.exports = router; 45 | -------------------------------------------------------------------------------- /views/users/login.ejs: -------------------------------------------------------------------------------- 1 | <%- include("../partials/header") %> <% if(error_message){ %> 2 |
3 | 4 |
5 | <%= error_message %> 6 |
7 |
8 | <% } %> 9 | 10 |
11 |
12 |

13 |
14 | Log-in to your account 15 |
16 |

17 |
18 |
19 |
20 |
21 | 22 | 28 |
29 |
30 |
31 |
32 | 33 | 39 |
40 | 41 | 44 |
45 |
46 | 47 |
48 |
49 | 50 |
51 | Don't have an account yet? Sign Up 52 |
53 |
54 |
55 | 56 | <%- include("../partials/footer") %> 57 | -------------------------------------------------------------------------------- /views/users/signup.ejs: -------------------------------------------------------------------------------- 1 | <%- include("../partials/header") %> <% if(error_message){ %> 2 |
3 | 4 |
5 | <%= error_message %> 6 |
7 |
8 | <% } %> 9 |
10 |
11 |

12 |
13 | Sign Up 14 |
15 |

16 |
17 |
18 |
19 |
20 | 21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 |
34 |
35 | 40 |
41 |
42 | 45 |
46 | 47 |
48 |
49 | 50 |
51 | Already have an account? Sign In 52 |
53 |
54 |
55 | 56 | <%- include("../partials/footer") %> 57 | -------------------------------------------------------------------------------- /public/stylesheets/app.css: -------------------------------------------------------------------------------- 1 | body > .grid { 2 | height: 100%; 3 | } 4 | .image { 5 | margin-top: -100px; 6 | } 7 | .column { 8 | max-width: 450px; 9 | } 10 | 11 | .hidden.menu { 12 | display: none; 13 | } 14 | 15 | .masthead.segment { 16 | min-height: 700px; 17 | padding: 1em 0em; 18 | } 19 | .masthead .logo.item img { 20 | margin-right: 1em; 21 | } 22 | .masthead .ui.menu .ui.button { 23 | margin-left: 0.5em; 24 | } 25 | .masthead h1.ui.header { 26 | margin-top: 3em; 27 | margin-bottom: 0em; 28 | font-size: 4em; 29 | font-weight: normal; 30 | } 31 | .masthead h2 { 32 | font-size: 1.7em; 33 | font-weight: normal; 34 | } 35 | 36 | .ui.vertical.stripe { 37 | padding: 8em 0em; 38 | } 39 | .ui.vertical.stripe h3 { 40 | font-size: 2em; 41 | } 42 | .ui.vertical.stripe .button + h3, 43 | .ui.vertical.stripe p + h3 { 44 | margin-top: 3em; 45 | } 46 | .ui.vertical.stripe .floated.image { 47 | clear: both; 48 | } 49 | .ui.vertical.stripe p { 50 | font-size: 1.33em; 51 | } 52 | .ui.vertical.stripe .horizontal.divider { 53 | margin: 3em 0em; 54 | } 55 | 56 | .quote.stripe.segment { 57 | padding: 0em; 58 | } 59 | .quote.stripe.segment .grid .column { 60 | padding-top: 5em; 61 | padding-bottom: 5em; 62 | } 63 | 64 | .footer.segment { 65 | padding: 5em 0em; 66 | } 67 | 68 | .secondary.pointing.menu .toc.item { 69 | display: none; 70 | } 71 | 72 | @media only screen and (max-width: 700px) { 73 | .ui.fixed.menu { 74 | display: none !important; 75 | } 76 | .secondary.pointing.menu .item, 77 | .secondary.pointing.menu .menu { 78 | display: none; 79 | } 80 | .secondary.pointing.menu .toc.item { 81 | display: block; 82 | } 83 | .masthead.segment { 84 | min-height: 350px; 85 | } 86 | .masthead h1.ui.header { 87 | font-size: 2em; 88 | margin-top: 1.5em; 89 | } 90 | .masthead h2 { 91 | margin-top: 0.5em; 92 | font-size: 1.5em; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | const bodyParser = require('body-parser'); 5 | const pool = require('./db'); 6 | const sessions = require('client-sessions'); 7 | const users = require('./routes/users'); 8 | const money = require('./routes/money'); 9 | const flash = require('connect-flash'); 10 | const cookieParser = require('cookie-parser'); 11 | const session = require('express-session'); 12 | 13 | app.set('view engine', 'ejs'); 14 | app.use(bodyParser.json()); 15 | app.use(bodyParser.urlencoded({ extended: true })); 16 | app.use(express.static(__dirname + '/public')); 17 | app.use('/public', express.static('public')); 18 | app.use(cookieParser()); 19 | app.use( 20 | session({ 21 | secret: 'flashSecret', 22 | saveUninitialized: true, 23 | resave: true, 24 | }) 25 | ); 26 | app.use(flash()); 27 | 28 | // cookie settings for authentication sessions 29 | app.use( 30 | sessions({ 31 | cookieName: 'cashAppSession', // cookie name dictates the key name added to the request object 32 | secret: 'cashAppSecret', // should be a large unguessable string 33 | duration: 10 * 60 * 1000, // 30 min 34 | httpOnly: false, //don't let JS code access cookies 35 | // secure: true, // only set cookies over https 36 | ephemeral: true, // destroy cookies when the browser closes 37 | }) 38 | ); 39 | 40 | app.use((req, res, next) => { 41 | if (!(req.cashAppSession && req.cashAppSession.userId)) { 42 | return next(); 43 | } 44 | pool.query( 45 | 'SELECT * FROM users WHERE id=$1', 46 | [req.cashAppSession.userId], 47 | (err, result) => { 48 | if (err) { 49 | return next(err); 50 | } 51 | if (!result.rows[0]) { 52 | return next(); 53 | } 54 | result.rows[0].password = undefined; 55 | req.user = result.rows[0]; 56 | res.locals.user = result.rows[0]; 57 | next(); 58 | } 59 | ); 60 | }); 61 | 62 | app.get('/', (req, res) => { 63 | res.render('home', { sucess_message: req.flash('sucess')[0] }); 64 | }); 65 | 66 | app.use('/users', users); 67 | app.use('/money', money); 68 | 69 | app.listen(process.env.PORT || 3000, (err, res) => { 70 | console.log('Server is running on port 3000'); 71 | }); 72 | -------------------------------------------------------------------------------- /views/money/dashboard.ejs: -------------------------------------------------------------------------------- 1 | <%- include("../partials/header") %> <% if(sucess_message){ %> 2 |
3 | 4 |
5 | <%= sucess_message %> 6 |
7 |
8 | <% } %> 9 | 10 |
11 |
Dashboard
12 |
13 |
14 |
Balance: $<%= balance %>
15 |
16 | 17 |
18 |
Transactions
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | <% for (i = 0; i < transactions.length; i++) { %> 32 | 33 | 34 | 35 | 36 | 37 | 38 | <% } %> 39 | 40 |
DateSender IDRecipient IDAmount
<%= transactions[i].date %><%= transactions[i].sender_id %><%= transactions[i].recipient_id %><%= transactions[i].amount %>
41 | 42 |
43 |
New Funds
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | <% for (i = 0; i < addFundsTransactions.length; i++) { %> 56 | 57 | 58 | 59 | 60 | 61 | <% } %> 62 | 63 |
DateBank IDAmount
<%= addFundsTransactions[i].date %><%= addFundsTransactions[i].bank %><%= addFundsTransactions[i].amount %>
64 | 65 |
66 |
Withdrawals
67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | <% for (i = 0; i < cashoutTransactions.length; i++) { %> 79 | 80 | 81 | 82 | 83 | 84 | <% } %> 85 | 86 |
DateBank IDAmount
<%= cashoutTransactions[i].date %><%= cashoutTransactions[i].bank %><%= cashoutTransactions[i].amount %>
87 | 88 | <%- include("../partials/footer") %> 89 | -------------------------------------------------------------------------------- /views/home.ejs: -------------------------------------------------------------------------------- 1 | <%- include("./partials/header") %> 2 | 3 | 4 | <% if(sucess_message){ %> 5 |
6 | 7 |
8 | <%= sucess_message %> 9 |
10 |
11 | <% } %> 12 |
13 |
14 |
15 |

16 | CashApp Clone 17 |

18 |

Send and receive money from anyone

19 |
20 | Get Started 21 | 22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 |

Send

32 |

33 | Send money through secure ACID transactions. 34 |

35 |

Request

36 |

37 | Request money and notify through email other registered users. 38 |

39 |

Add Funds

40 |

41 | Add funds from your bank account to make your first P2P transaction. 42 |

43 |

Cash Out

44 |

45 | Withdraw money from your account and send it to your bank. 46 |

47 |
48 |
49 | 53 |
54 |
55 |
56 |
57 | Get Started 58 |
59 |
60 |
61 |
62 | 63 | 86 |
87 | 88 | <%- include("./partials/footer") %> -------------------------------------------------------------------------------- /controllers/userController.js: -------------------------------------------------------------------------------- 1 | const userController = {}; 2 | const { validationResult } = require('express-validator'); 3 | const pool = require('../db'); 4 | const bcrypt = require('bcrypt'); 5 | 6 | //-------------------- 7 | // SIGNUP 8 | //-------------------- 9 | 10 | userController.getSignup = (req, res) => { 11 | res.render('users/signup', { 12 | error_message: req.flash('error')[0], 13 | }); 14 | }; 15 | 16 | userController.postSignup = async (req, res) => { 17 | // DATA VALIDATION 18 | 19 | const errors = validationResult(req); 20 | if (!errors.isEmpty()) { 21 | // console.log(errors.array()); 22 | // req.flash('error', errors.array()); 23 | return res.status(422).json({ errors: errors.array() }); 24 | } 25 | 26 | const { name, email, password, confirmPassword } = req.body; 27 | 28 | // CHECK IF EMAIL IS UNIQUE 29 | 30 | try { 31 | const emailUnique = await pool.query( 32 | 'SELECT email FROM users WHERE email=$1', 33 | [email] 34 | ); 35 | if (emailUnique.rows[0]) { 36 | req.flash('error', 'Email already being used'); 37 | return res.redirect('/users/signup'); 38 | } 39 | } catch (err) { 40 | console.log(err); 41 | } 42 | 43 | // PASSWORD MATCH 44 | try { 45 | if (password !== confirmPassword) { 46 | req.flash('error', 'Passwords must match. Please try again'); 47 | return res.redirect('signup/'); 48 | } 49 | } catch (err) { 50 | console.log(err); 51 | } 52 | 53 | //PASSWORD ENCRYPTION 54 | let encryptedPassword; 55 | try { 56 | encryptedPassword = await bcrypt.hash(password, 10); 57 | } catch (err) { 58 | console.log(err); 59 | } 60 | 61 | // SAVING NEW USER IN THE DATABASE 62 | let newUser; 63 | try { 64 | newUser = await pool.query( 65 | 'INSERT INTO users(name, email, password) VALUES ($1, $2, $3) RETURNING id', 66 | [name, email, encryptedPassword] 67 | ); 68 | // adding the user ID to the cookie in the response header 69 | req.cashAppSession.userId = newUser.rows[0].id; 70 | res.redirect('/money/'); 71 | } catch (err) { 72 | console.log(err); 73 | } 74 | 75 | //ADDING NEW USER TO THE BALANCE TABLE 76 | 77 | try { 78 | const balanceRow = await pool.query( 79 | 'INSERT INTO balance(user_id, balance) VALUES ($1, $2)', 80 | [newUser.rows[0].id, 0] 81 | ); 82 | } catch (err) { 83 | console.log(err); 84 | } 85 | }; 86 | 87 | //-------------------- 88 | // LOGIN 89 | //-------------------- 90 | 91 | userController.getLogin = (req, res) => { 92 | res.render('users/login', { 93 | csrfToken: req.csrfToken(), 94 | error_message: req.flash('error')[0], 95 | }); 96 | }; 97 | 98 | userController.postLogin = async (req, res) => { 99 | const { email, password } = req.body; 100 | 101 | // FINDING EMAIL IN THE DB 102 | let result; 103 | try { 104 | result = await pool.query('SELECT * FROM users WHERE email=$1', [email]); 105 | } catch (err) { 106 | console.log(err); 107 | } 108 | 109 | if (!result.rows[0]) { 110 | req.flash('error', 'Email not Found'); 111 | return res.redirect('/users/login'); 112 | } 113 | 114 | if (!bcrypt.compareSync(password, result.rows[0].password)) { 115 | req.flash('error', 'Wrong Password. Try again.'); 116 | return res.redirect('/users/login'); 117 | } 118 | 119 | // adding the user ID to the cookie in the response header 120 | req.cashAppSession.userId = result.rows[0].id; 121 | 122 | res.redirect('/money'); 123 | }; 124 | 125 | //-------------------- 126 | // LOGOUT 127 | //-------------------- 128 | 129 | userController.logout = (req, res) => { 130 | if (req.cookies.cashAppSession) { 131 | res.clearCookie('cashAppSession'); 132 | req.flash('sucess', 'Logged Out'); 133 | res.redirect('/'); 134 | } else { 135 | req.flash('sucess', 'No user logged in'); 136 | res.redirect('/'); 137 | } 138 | }; 139 | 140 | module.exports = userController; 141 | -------------------------------------------------------------------------------- /controllers/moneyController.js: -------------------------------------------------------------------------------- 1 | const moneyController = {}; 2 | 3 | const pool = require('../db'); 4 | const requestMoneyEmail = require('../email/nodemailer'); 5 | 6 | // ----------------- 7 | // DASHBOARD 8 | // ----------------- 9 | 10 | moneyController.dashboard = async (req, res) => { 11 | let balance; 12 | try { 13 | balance = await pool.query('SELECT balance FROM balance WHERE user_id=$1', [ 14 | req.user.id, 15 | ]); 16 | } catch (err) { 17 | console.log(err); 18 | } 19 | 20 | // User to user transactions 21 | let transactions; 22 | try { 23 | transactions = await pool.query( 24 | 'SELECT * FROM transactions WHERE sender_id=$1 OR recipient_id=$1', 25 | [req.user.id] 26 | ); 27 | } catch (err) { 28 | console.log(err); 29 | } 30 | 31 | // cashout transactions 32 | let cashoutTransactions; 33 | try { 34 | cashoutTransactions = await pool.query( 35 | 'SELECT * FROM cash_out WHERE user_id=$1', 36 | [req.user.id] 37 | ); 38 | } catch (err) { 39 | console.log(err); 40 | } 41 | 42 | // add funds transactions 43 | let addFundsTransactions; 44 | try { 45 | addFundsTransactions = await pool.query( 46 | 'SELECT * FROM add_funds WHERE user_id=$1', 47 | [req.user.id] 48 | ); 49 | } catch (err) { 50 | console.log(err); 51 | } 52 | 53 | res.render('money/dashboard', { 54 | balance: balance.rows[0].balance, 55 | transactions: transactions.rows, 56 | addFundsTransactions: addFundsTransactions.rows, 57 | cashoutTransactions: cashoutTransactions.rows, 58 | sucess_message: req.flash('sucess')[0], 59 | }); 60 | }; 61 | 62 | // ----------------- 63 | // TRANSFER MONEY TO A USER 64 | // ----------------- 65 | 66 | moneyController.getTransfer = (req, res) => { 67 | res.render('money/transfer', { error_message: req.flash('error')[0] }); 68 | }; 69 | 70 | moneyController.postTransfer = async (req, res) => { 71 | let { email, amount } = req.body; 72 | const sender_id = req.user.id; 73 | 74 | amount = parseFloat(amount); 75 | 76 | // getting recipient user id with the email 77 | const recipient_id = await pool.query('SELECT id FROM users WHERE email=$1', [ 78 | email, 79 | ]); 80 | 81 | if (!recipient_id.rows[0]) { 82 | req.flash('error', 'User not Registered'); 83 | return res.redirect('transfer/new'); 84 | } 85 | 86 | // getting current balance and calculating final balance 87 | const senderCurrentBalance = await pool.query( 88 | 'SELECT balance FROM balance WHERE user_id=$1', 89 | [sender_id] 90 | ); 91 | const recipientCurrentBalance = await pool.query( 92 | 'SELECT balance FROM balance WHERE user_id=$1', 93 | [recipient_id.rows[0].id] 94 | ); 95 | 96 | const senderFinalBalance = senderCurrentBalance.rows[0].balance - amount; 97 | 98 | const recipientFinalBalance = 99 | recipientCurrentBalance.rows[0].balance + amount; 100 | 101 | // checking if sender has enough funds 102 | if (senderFinalBalance < 0) { 103 | req.flash('error', "You don't have enough funds"); 104 | return res.redirect('transfer/new'); 105 | } 106 | 107 | pool.query('BEGIN', (err) => { 108 | if (err) { 109 | console.log(err); 110 | } 111 | // adding transaction to transactions table 112 | pool.query( 113 | 'INSERT INTO transactions (sender_id, recipient_id, amount) VALUES ($1, $2, $3)', 114 | [sender_id, recipient_id.rows[0].id, amount], 115 | (err) => { 116 | if (err) { 117 | console.log(err); 118 | } 119 | // adding amount to recipient balance 120 | pool.query( 121 | 'UPDATE balance SET balance=$1 WHERE user_id=$2', 122 | [recipientFinalBalance, recipient_id.rows[0].id], 123 | (err) => { 124 | if (err) { 125 | console.log(err); 126 | } 127 | // subtracking amount from the sender balance 128 | pool.query( 129 | 'UPDATE balance SET balance=$1 WHERE user_id=$2', 130 | [senderFinalBalance, sender_id], 131 | (err) => { 132 | if (err) { 133 | console.log(err); 134 | } 135 | pool.query('COMMIT', (err) => { 136 | if (err) { 137 | console.log(err); 138 | } 139 | req.flash('sucess', 'Transfer Completed'); 140 | res.redirect('/money/'); 141 | }); 142 | } 143 | ); 144 | } 145 | ); 146 | } 147 | ); 148 | }); 149 | }; 150 | 151 | // ----------------- 152 | // REQUEST MONEY FROM A USER 153 | // ----------------- 154 | 155 | moneyController.getRequestMoney = (req, res) => { 156 | res.render('money/request', { error_message: req.flash('error')[0] }); 157 | }; 158 | 159 | moneyController.postRequestMoney = async (req, res) => { 160 | let { email, amount } = req.body; 161 | 162 | amount = parseFloat(amount); 163 | 164 | let requesteeData; 165 | try { 166 | requesteeData = await pool.query('SELECT * FROM users WHERE email=$1', [ 167 | email, 168 | ]); 169 | 170 | if (!requesteeData.rows[0]) { 171 | req.flash('error', 'User not Found'); 172 | return res.redirect('request/new'); 173 | } 174 | } catch (err) { 175 | console.log(err); 176 | } 177 | 178 | let requestorData; 179 | try { 180 | requestorData = await pool.query('SELECT * FROM users WHERE id=$1', [ 181 | req.user.id, 182 | ]); 183 | } catch (err) { 184 | console.log(err); 185 | } 186 | 187 | try { 188 | await requestMoneyEmail( 189 | requesteeData.rows[0].name, 190 | requesteeData.rows[0].email, 191 | requestorData.rows[0].name, 192 | amount 193 | ); 194 | req.flash('sucess', "Your request was sent to the user's email"); 195 | res.redirect('/money/'); 196 | } catch (err) { 197 | console.log(err); 198 | } 199 | }; 200 | 201 | // ----------------- 202 | // ADD FUNDS 203 | // ----------------- 204 | 205 | moneyController.getAddFunds = (req, res) => { 206 | res.render('money/addFunds'); 207 | }; 208 | 209 | moneyController.postAddFunds = async (req, res) => { 210 | let { bank, amount } = req.body; 211 | const userId = req.user.id; 212 | 213 | amount = parseFloat(amount); 214 | 215 | const userCurrentBalance = await pool.query( 216 | 'SELECT balance FROM balance WHERE user_id=$1', 217 | [userId] 218 | ); 219 | const userFinalBalance = userCurrentBalance.rows[0].balance + amount; 220 | 221 | pool.query('BEGIN', (err) => { 222 | if (err) { 223 | console.log(err); 224 | } 225 | // add transaction to add_funds table 226 | pool.query( 227 | 'INSERT INTO add_funds (user_id, bank, amount) VALUES ($1, $2, $3)', 228 | [userId, bank, amount], 229 | (err) => { 230 | if (err) { 231 | console.log(err); 232 | } 233 | // add amount to the user account 234 | pool.query( 235 | 'UPDATE balance SET balance=$1 WHERE user_id=$2', 236 | [userFinalBalance, userId], 237 | (err) => { 238 | if (err) { 239 | console.log(err); 240 | } 241 | pool.query('COMMIT', (err) => { 242 | if (err) { 243 | console.log(err); 244 | } 245 | req.flash('sucess', 'Funds added sucessfully'); 246 | res.redirect('/money/'); 247 | }); 248 | } 249 | ); 250 | } 251 | ); 252 | }); 253 | }; 254 | 255 | // ----------------- 256 | // CASH OUT 257 | // ----------------- 258 | 259 | moneyController.getCashout = (req, res) => { 260 | res.render('money/cashout', { error_message: req.flash('error')[0] }); 261 | }; 262 | 263 | moneyController.postCashout = async (req, res) => { 264 | let { bank, amount } = req.body; 265 | const userId = req.user.id; 266 | 267 | amount = parseFloat(amount); 268 | 269 | const userCurrentBalance = await pool.query( 270 | 'SELECT balance FROM balance WHERE user_id=$1', 271 | [userId] 272 | ); 273 | 274 | const userFinalBalance = userCurrentBalance.rows[0].balance - amount; 275 | 276 | if (userFinalBalance < 0) { 277 | req.flash('error', 'User does not have enough funds'); 278 | return res.redirect('cashout/new'); 279 | } 280 | 281 | pool.query('BEGIN', (err) => { 282 | if (err) { 283 | console.log(err); 284 | } 285 | // add transaction to the cash_out table 286 | pool.query( 287 | 'INSERT INTO cash_out (user_id, bank, amount) VALUES ($1, $2, $3)', 288 | [userId, bank, amount], 289 | (err) => { 290 | if (err) { 291 | console.log(err); 292 | } 293 | // subtrack amount from the user account 294 | pool.query( 295 | 'UPDATE balance SET balance=$1 WHERE user_id=$2', 296 | [userFinalBalance, userId], 297 | (err) => { 298 | if (err) { 299 | console.log(err); 300 | } 301 | pool.query('COMMIT', (err) => { 302 | if (err) { 303 | console.log(err); 304 | } 305 | req.flash('sucess', 'Money was withdrawn sucessfully'); 306 | res.redirect('/money/'); 307 | }); 308 | } 309 | ); 310 | } 311 | ); 312 | }); 313 | }; 314 | 315 | module.exports = moneyController; 316 | --------------------------------------------------------------------------------