-
Notifications
You must be signed in to change notification settings - Fork 12
/
app.js
152 lines (120 loc) · 4.07 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
require('dotenv').config()
const express = require('express')
const formidable = require('express-formidable')
const { generateDuitNowStr } = require('duitnow-js')
const { randomUUID, createHash } = require('crypto')
const QRCode = require('easyqrcodejs-nodejs')
const app = express()
const port = 3001
let clients = [];
app.use(formidable())
app.engine('html', require('ejs').renderFile)
app.disable('view cache')
// Home page
app.get('/', (req, res) => {
const refid = randomUUID()
newClient = {
id: refid
}
clients.push(newClient)
res.render(__dirname + '/index.html', {refid: refid})
})
// DuitNow QR Image
app.get('/qr/:refid', async (req, res) => {
const refid = req.params.refid
const qrString = generateDuitNowStr({
app: process.env.DUITNOW_VENDOR,
account: process.env.DUITNOW_ACCOUNT,
category: process.env.DUITNOW_MERCHANT_CATEGORY,
ref5: refid,
ref6: process.env.DUITNOW_MERCHANT_NAME, // display in Razer report in Bill Name column
ref82: process.env.DUITNOW_REF82,
name: process.env.DUITNOW_MERCHANT_NAME // display in Bank statement
})
const qrCode = new QRCode({
text: qrString,
width: 512,
height: 512,
colorDark : '#FF076Aff',
colorLight : '#FFFFFF',
correctLevel : QRCode.CorrectLevel.H,
quietZone: 12,
quietZoneColor: '#FFFFFF',
logo: './images/duitnow-logo.png',
logoWidth: 69,
logoHeight: 75,
logoBackgroundColor: '#FFFFFF'
})
qrCode.toDataURL().then((base64data) => {
base64data = base64data.replace(/^data:image\/png;base64,/, '')
img = Buffer.from(base64data, 'base64')
res.writeHead(200, {
'Content-Type': 'image/png',
'Content-Length': img.length
});
res.end(img)
})
})
// Server-Sent Events
app.get('/sse/:refid', (req, res) => {
const refid = req.params.refid
console.log(`${refid} Connection opened`)
const headers = {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
'X-Accel-Buffering': 'no'
}
res.writeHead(200, headers)
const data = `id: ${refid}\n\n`;
res.write(data);
// save Response object to use later
const clientIndex = clients.findIndex(client => client.id == refid)
if (clientIndex >= 0)
clients[clientIndex].response = res
req.on('close', () => {
console.log(`${refid} Connection closed`)
clients = clients.filter(client => client.id !== refid)
})
})
// Short Polling // because SSE does not work on cloud
app.get('/poll/:refid', (req, res) => {
let message = ''
const refid = req.params.refid
const clientIndex = clients.findIndex(client => client.id == refid)
if (clientIndex >= 0 && clients[clientIndex].hasOwnProperty('message')) {
message = clients[clientIndex].message
clients[clientIndex].message = ''
}
res.send(message)
})
// Razer Callback URL
app.post('/ipn', (req, res) => {
console.log(req.fields)
// validate signature
const preSkey = createHash('md5').update(req.fields.tranID + req.fields.orderid + req.fields.status + req.fields.domain + req.fields.amount + req.fields.currency).digest('hex')
const sKey = createHash('md5').update(req.fields.paydate + req.fields.domain + preSkey + req.fields.appcode + process.env.RAZER_SECRET_KEY).digest('hex')
if (sKey !== req.fields.skey)
return res.status(400).send('Bad Request')
const refid = req.fields.orderid
const clientIndex = clients.findIndex(client => client.id == refid)
if (clientIndex < 0) return res.send('OK')
// build notification message
let message;
if (req.fields.error_desc) {
message = req.fields.error_desc
} else {
extraFields = JSON.parse(req.fields.extraP)
message = 'Received '+ req.fields.currency + req.fields.amount + ' via ' + extraFields.bank_issuer
}
// push message to SSE client
if (clients[clientIndex].hasOwnProperty('response'))
clients[clientIndex].response.write(`data: ${message}\n\n`)
// push message to Short Polling
clients[clientIndex].message = message
res.send('OK')
})
// Start server
app.listen(port, () => {
console.log(`App listening on port ${port}`)
})