<file_sep>exports.up = async function up(knex) {
    await knex.schema.createTable('users', table => {
        table.increments('id').unsigned().notNullable().primary();
        table.string('user_id', 20).unique().notNullable();
        table.string('email', 100).unique().notNullable();
        table.string('name', 100).notNullable();
        table.string('password', 100).unique().notNullable();
        table.float('balance').notNullable().defaultTo(0);
        table.enum('status', ['active','not_active']).notNullable().defaultTo('active');
        table.dateTime('created').notNullable().defaultTo(knex.fn.now());
    });
};
  
exports.down = async function down(knex) {
    await knex.schema.dropTable('users');
};
  <file_sep>const dotenv = require('dotenv');
dotenv.config({path: '../config/.env'});
const environment = process.env.NODE_ENV;
const config = require('./knexfile.js')[environment];
const pool = require('knex')(config);
module.exports = pool;<file_sep>const passport = require('passport');
const bcrypt = require('bcrypt');
var crypto = require('crypto');
const { usersModel, userBanksModel } = require('../database/models/user.model');
const { debitTransactionModel, creditTransactionModel } = require('../database/models/transaction.model');
const { getCurrentTime, idGenerator, issueJWT, generateReference } = require('../utils/helpers.utils');
const catchAsync = require('../utils/async.utils');
const { listBanks, createTransferRecipient, initializeTransaction, initiateTransfer } = require('../controllers/paystack.controller');

exports.createAccount = catchAsync ( async (req, res, next) => {

  	const { name, email, password } = req.body;

  	try {

		const password_hash = await bcrypt.hash(password, 12);
		const user_id = idGenerator();
        const created = getCurrentTime();

		await usersModel.query().insert({user_id, email, name, password:<PASSWORD>, created});

        req.result = {status:'success',message:'Account created successfully, please proceed to login',code:200};
		return next();

  	} catch (err) {

  		console.log(err);
  		req.result = {status: 'failed',message: 'Unable to perform request',code:500};
		return next();
  	}
});

exports.loginAccount = catchAsync ( async (req, res, next) => {
  	
  	try {

        const { email, password } = req.body;
    
        const { hash, user_id } = req.login_access;
    
        const compare_password = await bcrypt.compare(password, hash);
    
        if (compare_password == true) {
    
            const data = {key:user_id};
    
            const jwt = issueJWT(data, process.env.JWT_EXPIRES);
    
            req.result = {status:'success',message:'Logged in successfully',code:200,data:{token:jwt}};
            return next();
        }

        req.result = {status:'failed',message: 'Invalid login credentials',code:401};
        return next();

  	} catch (err) {

  		console.log(err);
  		req.result = {status: 'failed',message: 'Unable to perform request',code:500};
		return next();
  	}	
});

exports.checkAuthentication = passport.authenticate('jwt', { session: false });

exports.getBanks = catchAsync ( async (req, res, next) => {

    try {

        const { body: { message, data: banks } } = await listBanks('NGN');

        const data = banks.map(bank => {
            return {bank_name:bank.name,bank_code:bank.code,slug:bank.slug};
        });

        req.result = {status:'success',message,data,code:200};
		return next();

    } catch (err) {
        console.log(err);
  		req.result = {status: 'failed',message: 'Unable to perform request',code:500};
		return next();
    }
});

exports.createBeneficiary  = catchAsync ( async (req, res, next) => {

    const { bank_code, account_no: bank_account } = req.body;

    const { name, user_id } = req.user;

  	try {

        const created = getCurrentTime();

        const { body: { data } } = await createTransferRecipient({type:"nuban",name,account_number:bank_account,bank_code});

        await userBanksModel.query().insert({user_id, bank_code, bank_account, transfer_code:data.recipient_code, created});

        req.result = {status:'success',message:'Beneficiary successfully added',code:200};
		return next();

  	} catch (err) {

  		console.log(err);
  		req.result = {status: 'failed',message: 'Invalid account details',code:500};
		return next();
  	}

});

exports.getBeneficiaries  = catchAsync ( async (req, res, next) => {

    const { user_id } = req.user;

  	try {

        const data = await userBanksModel.query().select('transfer_code','bank_code','bank_account').where({user_id});        

        req.result = {status:'success',message:'Beneficiary successfully added',data,code:200};
		return next();

  	} catch (err) {

  		console.log(err);
  		req.result = {status: 'failed',message: 'Unable to perform request',code:500};
		return next();
  	}

});

exports.getAccountBalance = catchAsync ( async (req, res, next) => {

  	try {
        
        const { balance: account_balance } = req.user;

        req.result = {status:'success',message:'Balance retrieved successfully',data:{account_balance},code:200};
		return next();

  	} catch (err) {

  		console.log(err);
  		req.result = {status: 'failed',message: 'Unable to perform request',code:500};
		return next();
  	}

});

exports.fundAccount = catchAsync ( async (req, res, next) => {

    try {
      
        const { user_id, email  } = req.user;

        const { channel, amount } = req.body;

        const meta_data = JSON.stringify({data:req.user});

        const credit_amount = Math.abs(parseFloat(amount));

        const ref_code = generateReference();

        const channels = [channel];

        const params = {reference:ref_code,amount:`${credit_amount*100}`,email,callback_url:`${req.protocol}://${req.get('host')}/users/account/payment_success`,channels,metadata:meta_data}

        const { data: { data: { authorization_url }}  } = await initializeTransaction(params);

        await creditTransactionModel.query().insert({user_id, channel, ref_code, meta_data, amount:credit_amount, created:getCurrentTime()});

        req.result = {status:'success',message:'Complete your transaction',data:{authorization_url},code:200};
        return next();

    } catch (err) {
        console.log(err);
        req.result = {status: 'failed',message: 'Unable to perform request',code:500};
        return next();
    }
});

exports.transferFunds = catchAsync ( async (req, res, next) => {

    try {
      
        const { user_id } = req.user;

        const {transfer_id, amount} = req.transfer;

        const debit_amount = Math.abs(parseFloat(amount));

        const meta_data = JSON.stringify({recipient:transfer_id});

        const created = getCurrentTime();

        const ref_code = generateReference();

        const transaction = await creditTransactionModel.transaction(async trx => {

            await debitTransactionModel.query(trx).insert({user_id,trx_type:'transfer',ref_code,meta_data,amount,status:'completed',created});

            await usersModel.query(trx).where({user_id}).decrement('balance', debit_amount).limit(1);

            await usersModel.query(trx).where({user_id:transfer_id}).increment('balance', debit_amount).limit(1);

            await creditTransactionModel.query(trx).insert({user_id:transfer_id, channel:'transfer', ref_code, meta_data, amount:debit_amount,status:'completed',created});

        });

        req.result = {status:'success',message:'Transfer completed successfully',code:200};
        return next();

    } catch (err) {
        console.log(err.message);
        req.result = {status: 'failed',message: 'Unable to perform request',code:500};
        return next();
    }
});

exports.withdrawFunds = catchAsync ( async (req, res, next) => {

    try {
      
        const { user_id  } = req.user;

        const { transfer_code, amount } = req.body;

        const meta_data = JSON.stringify({data:{user_id,transfer:transfer_code}});

        const debit_amount = Math.abs(parseFloat(amount));

        const ref_code = generateReference();

        const params = {reference:ref_code,amount:debit_amount*100,metadata:meta_data,recipient:transfer_code,source:'balance'}

        const { body: { message, data } } = await initiateTransfer(params);

        await debitTransactionModel.query().insert({user_id, trx_type:'withdraw', ref_code, meta_data, amount:debit_amount, created:getCurrentTime()});

        req.result = {status:'success',message:'Your funds is on its way to your account',code:200};
        return next();

    } catch (err) {
        console.log(err);
        req.result = {status: 'failed',message:'You cannot initiate third party payouts as a starter business',code:500};
        return next();
    }
});

exports.checkPayment = catchAsync ( async (req, res, next) => {
    
    try {

        const hash = crypto.createHmac('sha512', process.env.PAYSTACK_SECRET_KEY).update(JSON.stringify(req.body)).digest('hex');

        if (hash == req.headers['x-paystack-signature']) {

            const { event } = req.body;

            if (event == 'charge.success') {

                const { data: { reference, amount, metadata: { data: meta_data } } } = req.body;

                const  { user_id } = meta_data;

                const credit_amount = amount/100;

                res.status(200);

                const transaction = await creditTransactionModel.transaction(async trx => {

                    await creditTransactionModel.query(trx).where({ref_code:reference}).patch({status:'completed',created:getCurrentTime()});

                    await usersModel.query(trx).where({user_id}).increment('balance', Math.abs(credit_amount)).limit(1);
                });
            }

            if (event == 'transfer.success') {

                const { data: { reference, amount, recipient: { metadata: {data: meta_data} } } } = req.body;

                const  { user_id } = meta_data;

                const debit_amount = amount/100;

                res.status(200);

                const transaction = await creditTransactionModel.transaction(async trx => {

                    await debitTransactionModel.query(trx).where({ref_code:reference}).patch({status:'completed',created:getCurrentTime()});

                    await usersModel.query(trx).where({user_id}).decrement('balance', Math.abs(debit_amount)).limit(1);

                });
            }

            if (event == 'transfer.failed' || event == 'transfer.failed') {

                res.status(200);

                await debitTransactionModel.query(trx).where({ref_code:reference}).patch({status:'failed',created:getCurrentTime()});
            }
        }

        res.status(400);

    } catch (err) {
        console.log(err);
        res.status(500);
    }
});<file_sep>### Postman URL
https://documenter.getpostman.com/view/10031264/U16kqQUD
<file_sep>module.exports = {
    apps: [
        {
            name: "nodejs-test-app",
            script: "./server.js",
            instances  : 1,
            error_file: './errors/err.log',
            out_file: './errors/out.log',
            log_file: './errors/combined.log',
            exec_mode  : "cluster",
            env: {
                ODE_ENV: "development",
            },
            env_staging: {
                NODE_ENV: "staging",
            },
            env_production: {
                NODE_ENV: "production",
            }
        }
    ]
}<file_sep>const errors = require('../controllers/error.controller');

const sendErrorDev = (err, req, res) => {
	
	if (req.originalUrl .startsWith('/api')) {

		return res.status(err.statusCode).json({
	        status: err.status,
	        error:err,
	        message: err.message,
	        stack: err.stack
	    })
	}

	return res.status(err.statusCode).send('<h1 style="font-weight:bold;">Oops...!!!!<h1>');
}

const sendErrorStag = (err, req, res) => {
	
	if (req.originalUrl .startsWith('/api')) {

		if (err.isOperational) {

			return res.status(err.statusCode).json({
		        status: err.status,
		        message: err.message,
		    })
		}

		return res.status(500).json({
	        status: 'error',
	        message: "Service unavailable"
	    });
	}

	return res.status(err.statusCode).send('<h1 style="font-weight:bold;">Oops...!!!!<h1>');
}

const sendErrorPro = (err, req, res) => {

	if (req.originalUrl .startsWith('/api')) {

		if (err.isOperational) {

			return res.status(err.statusCode).json({
		        status: err.status,
		        message: err.message
		    })
		}

		return res.status(500).json({
	        status: 'error',
	        message: "Service unavailable"
	    });

	}

	return res.status(err.statusCode).send('<h1 style="font-weight:bold;">Oops...!!!!<h1>');	
}

module.exports = (err, req, res, next) => {
	
	err.statusCode = err.statusCode || 500;
	err.status = err.status || 'error'

	if (process.env.NODE_ENV === 'development') {
		sendErrorDev(err, req, res);
	}

	if (process.env.NODE_ENV === 'staging') {
		sendErrorStag(err, req, res);
	}

	if (process.env.NODE_ENV === 'production') {

		let error = {...err};
		error.message = err.message;
	    sendErrorPro(error, req, res);
	}
}<file_sep>exports.up = async function up(knex) {

    await knex.schema.createTable('beneficiary_banks', table => {
        table.increments('id').unsigned().notNullable().primary();
        table.string('user_id', 20).notNullable();
        table.string('bank_code', 10).notNullable();
        table.string('bank_account', 20).notNullable();
        table.string('transfer_code', 100).unique().notNullable();
        table.dateTime('created').notNullable().defaultTo(knex.fn.now());
        
        table.foreign('user_id').references('users.user_id');
    });
};
  
exports.down = async function down(knex) {
    await knex.schema.dropTable('beneficiary_banks');
};
  <file_sep>const fs = require('fs');
const path = require('path');

const { usersModel } = require('../database/models/user.model');

const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;

const pathToKey = path.join(__dirname, '../config', 'id_rsa_pub.pem');
const PUB_KEY = fs.readFileSync(pathToKey, 'utf8');

const options = {
	jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
	secretOrKey:PUB_KEY,
	algorithms: [process.env.JWT_ALOGRITHM],
	ignoreExpiration:false
};

const strategy = new JwtStrategy(options, async (payload, done) => {

	const {key} = payload;

	try {

		const get_user = usersModel.query().select('user_id','name','email','balance','status').where({user_id:key}).limit(1);
		
		get_user.then((user) => {

			if (user) {
	
				const {user_id, name, email, balance} = user[0]
	
				return done(null, {user_id,name,email,balance});
				
			}else{
				return done(null, false, {message:"Invalid credentials"});
			}
		})
		.catch(err => done(err, null, {message:"Invalid login credentials"}))
		
	} catch (err) {
		console.log(err)
		return done(null, false, {message:"Invalid credentials"});
	}
});

module.exports = (passport) => {
	passport.use(strategy);
}<file_sep>const { body, param, validationResult } = require('express-validator');
const {  usersModel, userBanksModel } = require('../database/models/user.model');

exports.validateAccount = [
    body('name').trim().escape().not().isEmpty().withMessage('Full name is required').matches(/([A-Za-z]{1})([A-Za-z]+)(\s)([A-Za-z]{1})([A-Za-z]+){1}/)
    .withMessage('Please provide your valid full name').bail(),
    body('email', 'Provide a valid email address').trim().not().isEmpty().exists().isEmail().normalizeEmail()
    .custom( async (value, { req }) => {

        try {
            const check = await usersModel.query().where({email:value}).select('id').limit(1);
            if (check.length > 0) {
                return Promise.reject('Email is already in use');
            }else {
                return;
            }
        } catch (err) {
            console.log(err);
            return Promise.reject('Unable to perform request');
        }
    }),
    body('password').trim().escape().not().isEmpty().withMessage('Password is required').isLength({min:8}).withMessage('Password must be at least 8 characters long')
    .matches(/^.*(?=.{6,})(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).*$/).withMessage('Password must contain a number and an uppercase letter').bail(),
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            const extractedErrors = [];
            errors.array({ onlyFirstError: true }).map(err => extractedErrors.push(err.msg));
            return res.status(422).json({status:'failed',message:'All account data is required',errors:extractedErrors});
        }
        next();
    }
]

exports.validateLogin = [
    body('email', 'Provide a valid email address').trim().not().isEmpty().exists().isEmail().normalizeEmail()
    .custom( async (value, { req }) => {

        try {
            const check = await usersModel.query().where({email:value}).select('user_id','password').limit(1);
            if (check.length < 1) {
                return Promise.reject('Your credentials did not match any of our records');
            }else {
                req.login_access = {hash:check[0].password,user_id:check[0].user_id};
                return;
            }
        } catch (err) {
            return Promise.reject('Unable to perform request');
        }
    }),
    body('password').trim().escape().not().isEmpty().withMessage('Password is required').isLength({min:8}).withMessage('Invalid Password')
    .matches(/^.*(?=.{6,})(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).*$/).withMessage('Invalid Password').bail(),
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            const extractedErrors = [];
            errors.array({ onlyFirstError: true }).map(err => extractedErrors.push(err.msg));
            return res.status(422).json({status:'failed',message:'Invalid login data',errors:extractedErrors});
        }
        next();
    }
]

exports.validateBeneficiary = [
    body('bank_code').trim().escape().not().isEmpty().withMessage('Bank code is required').bail(),
    body('account_no','Invalid bank account number').trim().escape().not().isEmpty().withMessage('Bank account number is required')
    .isLength({min:10,max:11}).isNumeric().withMessage('Invalid bank account number')
    .custom( async (value, { req }) => {

        try {
            const check = await userBanksModel.query().where({user_id:req.user.user_id,bank_account:value}).select('id').limit(1);
            if (check.length == 1) {
                return Promise.reject('Beneficiary already added');
            }
            return;
        } catch (err) {
            return Promise.reject('Unable to perform request');
        }
    }),
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            const extractedErrors = [];
            errors.array({ onlyFirstError: true }).map(err => extractedErrors.push(err.msg));
            return res.status(422).json({status:'failed',message:'Invalid beneficiary data',errors:extractedErrors});
        }
        next();
    }
]

exports.validateFundAccount = [
    body('channel').trim().escape().not().isEmpty().withMessage('funding channel is required')
    .custom( async (value, { req }) => {

        if (value != 'card' && value != 'bank_transfer') {
            return Promise.reject('channel can only be card or bank_transfer');
        }
        return;
    }),
    body('amount','amount is required').trim().escape().not().isEmpty().withMessage('amount is required').isNumeric().withMessage('Invalid amount').bail(),
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            const extractedErrors = [];
            errors.array({ onlyFirstError: true }).map(err => extractedErrors.push(err.msg));
            return res.status(422).json({status:'failed',message:'Invalid funding data',errors:extractedErrors});
        }
        next();
    }
]

exports.validateTransfer = [
    body('email', 'Please provide a valid email address').trim().not().isEmpty().withMessage('Please provide a valid email address').exists().isEmail().normalizeEmail()
    .custom( async (value, { req }) => {
        try {
            const check = await usersModel.query().where({email:value}).andWhere('user_id','!=', req.user.user_id).select('user_id').limit(1);
            if (check.length < 1) {
                return Promise.reject('Email does not exist');
            }else {
                req.transfer = {transfer_id:check[0].user_id,amount:req.body.amount};
                return;
            }
        } catch (err) {
            return Promise.reject('Unable to perform request');
        }
    }),
    body('amount','amount is required').trim().escape().not().isEmpty().withMessage('amount is required').isNumeric().withMessage('Invalid amount')
    .custom( async (value, { req }) => {
        try {
            const check = await usersModel.query().where('user_id','=', req.user.user_id).select('balance').limit(1);
            if (check.length < 1) {
                return Promise.reject('Invalid request');
            }else {

                const { balance } = check[0];

                if (Math.abs(balance) < value || balance == 0){
                    return Promise.reject('Insufficient balance');
                }else{
                    return;
                }
            }
        } catch (err) {
            return Promise.reject('Unable to perform request');
        }
    }),
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            const extractedErrors = [];
            errors.array({ onlyFirstError: true }).map(err => extractedErrors.push(err.msg));
            return res.status(422).json({status:'failed',message:'Invalid transfer data',errors:extractedErrors});
        }
        next();
    }
]

exports.validateWithdrawal = [
    body('transfer_code', 'Please provide transfer code').trim().escape().not().isEmpty()
    .custom( async (value, { req }) => {
        try {
            const check = await userBanksModel.query().where({transfer_code:value}).andWhere('user_id','=', req.user.user_id).select('id').limit(1);
            if (check.length < 1) {
                return Promise.reject('Beneficiary does not exist');
            }else {
                req.transfer = {transfer_id:check[0].user_id,amount:req.body.amount};
                return;
            }
        } catch (err) {
            return Promise.reject('Unable to perform request');
        }
    }),
    body('amount','amount is required').trim().escape().not().isEmpty().withMessage('amount is required').isNumeric().withMessage('Invalid amount')
    .custom( async (value, { req }) => {
        try {
            const check = await usersModel.query().where('user_id','=', req.user.user_id).select('balance').limit(1);
            if (check.length < 1) {
                return Promise.reject('Invalid request');
            }else {

                const { balance } = check[0];

                if (Math.abs(balance) < value || balance == 0){
                    return Promise.reject('Insufficient balance');
                }else{
                    return;
                }
            }
        } catch (err) {
            return Promise.reject('Unable to perform request');
        }
    }),
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            const extractedErrors = [];
            errors.array({ onlyFirstError: true }).map(err => extractedErrors.push(err.msg));
            return res.status(422).json({status:'failed',message:'Invalid transfer data',errors:extractedErrors});
        }
        next();
    }
]<file_sep>const express = require('express');
const ratelimit = require('express-rate-limit');
const { validateAccount, validateLogin, validateBeneficiary, validateFundAccount, validateTransfer, validateWithdrawal } = require('../middleware/validator.middleware');
const { response } = require('../middleware/response.middleware');
const { limiter } = require('../utils/helpers.utils');

const userController = require('../controllers/user.controller');

const router = express.Router();

router.use(express.json({limit: '10Kb'}));

router.post('/account/create', limiter(10, 36000), validateAccount, userController.createAccount, response);
router.post('/account/login', limiter(10, 36000), validateLogin, userController.loginAccount, response);

router.use(userController.checkAuthentication);

router.get('/account/banks', userController.getBanks, response);

router.route('/account/beneficiary')
.get(limiter(10, 36000), userController.getBeneficiaries, response)
.post(limiter(10, 36000), validateBeneficiary, userController.createBeneficiary, response);

router.get('/account/balance', limiter(10, 36000), userController.getAccountBalance, response);

router.post('/account/fund', limiter(10, 36000), validateFundAccount, userController.fundAccount, response);

router.post('/account/transfer', limiter(10, 36000), validateTransfer, userController.transferFunds, response);

router.post('/account/withdraw', limiter(10, 36000), validateWithdrawal, userController.withdrawFunds, response);

module.exports = router;<file_sep>const path = require('path');
const express = require('express');
const logger = require('morgan');
const xss = require('xss-clean');
const helmet = require('helmet');
const hpp = require('hpp');
const compression = require('compression');
const cors = require('cors');
const passport = require('passport');
const appError = require('./controllers/error.controller');
const globalErrorHandler = require('./middleware/error.middleware');

const userRoutes = require('./routes/user.routes');
const { checkPayment } = require('./controllers/user.controller')

const app = express();

app.enable("trust proxy");

app.set('view engine', 'pug');
app.set('views', path.join(__dirname, 'views'));

app.use(express.static(path.join(__dirname, 'public')));

app.use(cors());

app.options('*', cors());

app.use(helmet());

app.use(xss());

app.use(hpp());

app.use(compression());

app.use(express.json());

app.use(express.urlencoded({extended: true}));

if (process.env.NODE_ENV != 'production') {
    app.use(logger('dev'));
}

require('./middleware/passport.middleware')(passport);

app.get('/users/account/payment_success', (req, res, next) => {
    res.status(200).render('payment_success', {message: 'Account successfully funded'});
});

app.use('/webhook/payment', checkPayment);

app.use('/api/v1/users', userRoutes);


app.all('*', (req, res, next) => {
	next(new appError(`Requested resource not available`, 404));
})

app.use(globalErrorHandler);

module.exports = app;
<file_sep>const fs = require('fs');
const dotenv = require('dotenv');
dotenv.config({path: '../config/.env'});

module.exports = {
  development: {
    client: process.env.DB_DIALECT,
    connection: {
      host: process.env.DB_HOST,
      port: parseInt(process.env.DB_PORT),
      user: process.env.DB_USERNAME,
      password: <PASSWORD>,
      database: process.env.DB_NAME
    },
    pool: {
      min: parseInt(process.env.DB_POOL_MIN), 
      max: parseInt(process.env.DB_POOL_MAX),
      createTimeoutMillis: 3000,
      acquireTimeoutMillis: 30000,
      idleTimeoutMillis: 30000,
      reapIntervalMillis: 1000,
      createRetryIntervalMillis: 100,
      propagateCreateError: false
    },
    migrations: {directory: './migrations', tableName: 'migrations'},
    seeds:{directory: './seeds'}
  },
  staging: {
    client: process.env.DB_DIALECT,
    connection: {
      host: process.env.DB_HOST,
      port: parseInt(process.env.DB_PORT),
      user: process.env.DB_USERNAME,
      password: <PASSWORD>,
      database: process.env.DB_NAME
    },
    ssl: { ca:fs.readFileSync(`${__dirname}/../config/cert.crt`)},
    pool: {
      min: parseInt(process.env.DB_POOL_MIN), 
      max: parseInt(process.env.DB_POOL_MAX),
      createTimeoutMillis: 3000,
      acquireTimeoutMillis: 30000,
      idleTimeoutMillis: 30000,
      reapIntervalMillis: 1000,
      createRetryIntervalMillis: 100,
      propagateCreateError: false
    },
    migrations: {directory: './migrations',tableName: 'migrations'},
    seeds:{directory: './seeds'}
  },
  production: {
    client: process.env.DB_DIALECT,
    connection: {
      host: process.env.DB_HOST,
      port: parseInt(process.env.DB_PORT),
      user: process.env.DB_USERNAME,
      password: <PASSWORD>,
      database: process.env.DB_NAME
    },
    ssl: { ca:fs.readFileSync(`${__dirname}/../config/cert.crt`)},
    pool: {min: parseInt(process.env.DB_POOL_MIN), max: parseInt(process.env.DB_POOL_MAX)},
    migrations: { directory: './migrations', tableName: 'migrations'}
  }
};<file_sep>const Model = require('../objection');

exports.creditTransactionModel = class creditTransaction extends Model {
    
    static get tableName() {
      return 'credit_transactions';
    }
  
    static get idColumn() {
      return 'ref_code';
    }

    static get jsonSchema() {
      return {
        type: 'object',
        required: ['user_id', 'channel', 'ref_code', 'meta_data', 'amount', 'created'],
        properties: {
          user_id: { type: 'string' },
          channel: {type: 'string'},
          ref_code: {type: 'string'},
          meta_data: {type: ['string', null]},
          amount: {type: 'number'},
          captured: {type: Date}
        }
      };
    }
}

exports.debitTransactionModel = class debitTransaction extends Model {
    
    static get tableName() {
      return 'debit_transactions';
    }
  
    static get idColumn() {
      return 'ref_code';
    }

    static get jsonSchema() {
      return {
        type: 'object',
        required: ['user_id', 'trx_type', 'ref_code', 'meta_data', 'amount', 'created'],
        properties: {
          user_id: { type: 'string' },
          trx_type: {type: 'string'},
          ref_code: {type: 'string'},
          meta_data: {type: ['string', null]},
          amount: {type: 'number'},
          captured: {type: Date}
        }
      };
    }
}