Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
/variables.env
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
## Due: 22nd June 2018 or Earlier
#### This code challenge is due on the 22nd of June or earlier.


## How to test the application
1. Open the [africastalking sanbox simulator](https://simulator.africastalking.com:1517) in your browser.
2. Select your country and choose a phone number on the simulator page to test application with.
3. click on Launch
4. Select the USSD option and used the USSD code * 384 *41919#.
5. Interact with ussd with the appropriate response
6. Test bank checkout with option 3[ Join Agbetuntu]
7. Test out call/ voice service with 4

**Merci!!!**

## Simple Unchanging Rules
The code challenge is and will always be judged using the following criteria
- A Correct fork, branch and pull request
Expand Down
46 changes: 46 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const bodyParser = require('body-parser');

var indexRouter = require('./routes/index');
var ussdRouter = require('./routes/ussd');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/ussd', ussdRouter);

//Handle get to /sms
app.use(function(req, res, next) {
res.end('This is the ussd endpoint')
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

// render the error page
res.status(err.status || 500);
res.render('error');
});

module.exports = app;
26 changes: 26 additions & 0 deletions config/start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const mongoose = require('mongoose');

// import environmental variables from our variables.env file
require('dotenv').config({ path: 'variables.env' });

// Connect to our Database and handle any bad connections
mongoose.connect(process.env.DATABASE).then(()=>{
console.log('connection to DB successful')
}).catch(err=>{
console.error(`Error connecting to DB→ ${err.message}`)
});
mongoose.Promise = global.Promise;

mongoose.connection.on('error', (err) => {
console.error(`Error connecting to DB→ ${err.message}`);
});


// Start our app!
const app = require('../app');
app.set('port', process.env.PORT || 7777);
const server = app.listen(app.get('port'), () => {
console.log(`Express running → PORT ${server.address().port}`);
});

// process.on('SIGINT', () => { console.log("Bye bye!"); process.exit(); });
23 changes: 23 additions & 0 deletions controllers/ussdController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
var _ = require('lodash')
var { User } = require('../models/User')
var ussdHandler = require('../helpers/ussdProcessor')

exports.init = (req, res) => {
// Select needed properties from post object
var body = _.pick(req.body, ['sessionId', 'serviceCode', 'phoneNumber', 'text'])

// Authenticate User
// Check if session exists
var query = User.find({ 'sessionId': body.sessionId })
if (!query) {
var user = new User(body)
user.save()
.then(() => {
console.log('User Saved successfully')
}).catch((e) => {
console.log(e.message)
})
}

ussdHandler.processUSSD(body, res)
}
51 changes: 51 additions & 0 deletions helpers/bankCheckoutFunctions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require('dotenv').config({ path: 'variables.env' })
exports.selectBank = (res) => {
let response = `CON Select Your Bank
1. FCMB
2. Zenith Bank
3. Access Bank
4. Providus Nigeria
5. Sterling Nigeria`
res.send(response)
}

exports.initiateBankCheckout = (payments, accountNumber, selectedBank, transactionId, res) => {
let bankCheckoutOptions = {
'username': 'sandbox',
'productName': 'Agbetuntu',
'bankAccount': {
'accountName': 'Your Test Account',
'accountNumber': '1231231231',
'bankCode': 234001,
'dateOfBirth': '2017-11-22'
},
'currencyCode': 'NGN',
'amount': 500.5,
'narration': 'Join Agbebuntu Fee'
}
payments.bankCheckout(bankCheckoutOptions)
.then((data) => {
process.env.transactionId = data.transactionId
console.log(data)
res.send(`CON OTP code for transaction of ID: ${transactionId} has been sent.
Current status: ${data.description}.
Kindly enter your OTP Code to validate transaction`)
})
.catch((e) => {
console.log(e)
res.send('END Oooooops!!! Your OTP Generation has failed at this time. Try again later')
console.log(e.message)
}
)
}

exports.validateTransaction = (payments, validationOptions, res) => {
payments.validateBankCheckout(validationOptions)
.then((data) => {
res.send(`END Validation ${data.status}
${data.description}`)
})
.catch((e) => {
res.send('END OTP validation failed')
})
}
21 changes: 21 additions & 0 deletions helpers/callFunctions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
exports.initiateCall = (voice, dialerNumber, destPhoneNumber, callTextResponse, res) => {
voice.call({
callFrom: dialerNumber, // My AT virtual number
callTo: destPhoneNumber
})
.then(function (call) {
if (call.isActive == 1) {
let response = '<?xml version="1.0" encoding="UTF-8"?>'
response += '<Response>'
response += '<Say>' + callTextResponse + '</Say>'
response += '</Response>'
res.send(response)
}
res.send(`END Call has been initiated successfully.
Status: ${call.entries[0].status}
You'd receive a call shortly.`)
})
.catch(function (error) {
console.log(error.message)
})
}
5 changes: 5 additions & 0 deletions helpers/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
dialerNumber: '+2348141919419',
callTextResponse: 'Its always fun to tinker with new technology',
bankList: ['234001', '234002', '234003', '234007', '234010']
}
100 changes: 100 additions & 0 deletions helpers/ussdProcessor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const constants = require('./constants')
const callHandler = require('./callFunctions')
const bankCheckoutHandler = require('./bankCheckoutFunctions')
require('dotenv').config({ path: 'variables.env' })
const AfricasTalking = require('africastalking')(
{
apiKey: process.env.APIKEY,
username: 'sandbox'
}
)
const voice = AfricasTalking.VOICE
const payments = AfricasTalking.PAYMENTS
let selectedBank = ''
let accountNumber = ''
let userSelection = ''
let phoneNumber = ''
let transactionId = process.env.transactionId

exports.processUSSD = (body, res) => {
phoneNumber = body.phoneNumber
let seperatedBody = body.text.split('*')
let userResponse = seperatedBody[seperatedBody.length - 1]
console.log(body.text)
if (userResponse.length == 10) {
accountNumber = userResponse
console.log(accountNumber)
bankCheckoutHandler.initiateBankCheckout(payments, accountNumber, selectedBank, transactionId, res)
} else if (body.text.length >= 16) {
let seperatedBody = body.text.split('*')
let otp = seperatedBody[seperatedBody.length - 1]
console.log(process.env.transactionId, otp)
if (process.env.transactionId != undefined) {
bankCheckoutHandler.validateTransaction(payments, {
transactionId: process.env.transactionId,
otp: otp
}, res)
} else {
res.send('END Something went wrong with your OTP generation.Please try again later')
}
} else {
switch (body.text) {
case '' : {
// This is the first request. Note how we start the response with CON
let response = `CON What would you like to do
1. My Co-operative
2. Wazobia Loans
3. Join Agbetuntu
4. Request a call`
res.send(response)
break
}
case '1' : {
let response = `CON Select My Co-operative Option
1. Check Balance
2. Request Loan
3. Make Deposit`
res.send(response)
break
}
case '2' : {
let response = `CON Select Wazobia Loans Option
1. Register
2. Repay Loan
3. Make Deposit
4. Request Loan
5. Request a Call`
res.send(response)
break
}
case '3' : {
bankCheckoutHandler.selectBank(res)
break
}
case '3*1':
case '3*2':
case '3*3':
case '3*4':
case '3*5': {
userSelection = body.text.slice(-1)

if (userSelection > 0 && userSelection <= 5) {
selectedBank = constants.bankList[userSelection - 1]
console.log(selectedBank)
res.send('CON Enter your 10 digit Account Number')
} else {
res.send('END Wrong input')
}
break
}
case '4' : {
callHandler.initiateCall(voice, constants.dialerNumber, phoneNumber, constants.callTextResponse, res)
break
}
default : {
res.send('END We will be implementing that in the near future. Watch out!')
break
}
}
}
}
29 changes: 29 additions & 0 deletions models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const mongoose = require('mongoose')
// const _ = require('lodash')

var UserSchema = new mongoose.Schema({
phoneNumber: {
type: String,
required: true,
trim: true,
minlength: 8,
unique: true
},
sessionId: {
type: String,
required: true
}
})

UserSchema.methods.toJSON = function () {
var user = this
var userObject = user.toObject()

return _.pick(userObject, ['phoneNumber', 'session_id'])
}

let User = mongoose.model('Users', UserSchema)

module.exports = {
User
}
Loading