node-paytmpg 5.3.2 → 6.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/controllers/adapters/payu.js +251 -0
- package/app/controllers/np_user.controller.js +88 -79
- package/app/controllers/payment_controller.js +213 -84
- package/app/models/np_multidbplugin.js +111 -101
- package/app/views/init.hbs +85 -80
- package/app/views/layouts/index.hbs +46 -51
- package/app/views/result.hbs +43 -47
- package/example.js +33 -50
- package/index.js +75 -15
- package/lib/config/buildConfig.js +113 -0
- package/lib/config/defaults.js +37 -0
- package/lib/config/validator.js +103 -0
- package/lib/services/database.service.js +153 -0
- package/lib/utils/id-generator.js +30 -0
- package/lib/utils/sanitizer.js +25 -0
- package/package.json +44 -42
- package/public/css/style.css +373 -203
package/example.js
CHANGED
|
@@ -1,51 +1,34 @@
|
|
|
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
|
-
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const { SQLiteDB } = require('multi-db-orm');
|
|
3
|
+
const { createPaymentMiddleware } = require('./index');
|
|
4
|
+
|
|
5
|
+
try { require('dotenv').config(); } catch (e) { }
|
|
6
|
+
|
|
7
|
+
const app = express();
|
|
8
|
+
|
|
9
|
+
// Local SQLite sample DB via multi-db-orm; swap for your own adapter
|
|
10
|
+
const db = new SQLiteDB('test.db');
|
|
11
|
+
|
|
12
|
+
const payment = createPaymentMiddleware({
|
|
13
|
+
host_url: process.env.NP_HOST_URL || 'http://localhost:5543',
|
|
14
|
+
path_prefix: process.env.NP_PATH_PREFIX || '_pay',
|
|
15
|
+
homepage: '/',
|
|
16
|
+
payu_url: 'https://secure.payu.in', // use https://test.payu.in for sandbox
|
|
17
|
+
MID: process.env.NP_MID || '12345',
|
|
18
|
+
WEBSITE: process.env.NP_WEBSITE || 'WEBSTAGING',
|
|
19
|
+
KEY: process.env.NP_KEY || 'abcdef',
|
|
20
|
+
SECRET: process.env.NP_SECRET || 'abcdef', // salt for payu / razor
|
|
21
|
+
CHANNEL_ID: process.env.NP_CHANNEL_ID || 'WAP',
|
|
22
|
+
INDUSTRY_TYPE_ID: process.env.NP_INDUSTRY_TYPE_ID || 'Retail',
|
|
23
|
+
theme: {
|
|
24
|
+
primary: '#231530',
|
|
25
|
+
accent: '#5ce1e6',
|
|
26
|
+
},
|
|
27
|
+
brand: 'DemoPay',
|
|
28
|
+
}, db);
|
|
29
|
+
|
|
30
|
+
app.use(payment);
|
|
31
|
+
|
|
32
|
+
app.listen(process.env.PORT || 5543, function () {
|
|
33
|
+
console.log('Server started at', process.env.PORT || 5543);
|
|
29
34
|
});
|
|
30
|
-
|
|
31
|
-
if(process.env.CONFIG){
|
|
32
|
-
|
|
33
|
-
app.set('np_config', JSON.parse(process.env.CONFIG));
|
|
34
|
-
console.log('using config from env',process.env.CONFIG);
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
require('./index')(app,express)
|
|
39
|
-
|
|
40
|
-
app.all('/',function(req,res)
|
|
41
|
-
{
|
|
42
|
-
res.redirect('/_pay/init')
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
app.listen(process.env.PORT || 5542,function()
|
|
47
|
-
{
|
|
48
|
-
|
|
49
|
-
console.log("Server Started At ",process.env.PORT || 5542)
|
|
50
|
-
|
|
51
|
-
})
|
package/index.js
CHANGED
|
@@ -1,23 +1,83 @@
|
|
|
1
|
-
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const bodyParser = require('body-parser');
|
|
4
|
+
const exphbs = require('express-handlebars');
|
|
5
|
+
const mongoose = require('mongoose');
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
const buildConfig = require('./lib/config/buildConfig');
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Creates an isolated payment middleware that can be mounted on any Express app.
|
|
11
|
+
* This keeps the payment creation/verification logic intact while exposing a cleaner API.
|
|
12
|
+
*
|
|
13
|
+
* @param {object} userConfig - configuration overrides (gateway keys, branding, hooks, etc.)
|
|
14
|
+
* @param {object} db - optional multi-db-orm instance; if omitted and db_url is provided, MongoDB is used
|
|
15
|
+
* @returns {import('express').Application} configured sub-application ready to mount
|
|
16
|
+
*/
|
|
17
|
+
function createPaymentMiddleware(userConfig = {}, db) {
|
|
18
|
+
const config = buildConfig(userConfig);
|
|
19
|
+
const subApp = express();
|
|
9
20
|
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
// expose config + optional db handle for downstream controllers
|
|
22
|
+
subApp.set('np_config', config);
|
|
23
|
+
subApp.locals.theme = config.theme || {};
|
|
24
|
+
subApp.locals.brand = config.brand || 'Secure Pay';
|
|
25
|
+
subApp.locals.logo = config.logo;
|
|
26
|
+
subApp.locals.themeName = config.themeName || (config.theme && config.theme.name) || 'dark';
|
|
27
|
+
if (config.db_url) {
|
|
28
|
+
mongoose.Promise = global.Promise;
|
|
29
|
+
mongoose.connect(config.db_url, {
|
|
30
|
+
useUnifiedTopology: true,
|
|
31
|
+
useNewUrlParser: true,
|
|
32
|
+
}).then(() => {
|
|
33
|
+
console.log('PaytmPG : Connected to MongoDB');
|
|
34
|
+
}).catch(err => {
|
|
35
|
+
console.log('PaytmPG : Failed to connect MongoDB', err);
|
|
36
|
+
});
|
|
37
|
+
} else if (db) {
|
|
38
|
+
subApp.multidborm = db;
|
|
39
|
+
}
|
|
12
40
|
|
|
13
|
-
|
|
41
|
+
// view engine + theming
|
|
42
|
+
const viewRoot = config.templateDir
|
|
43
|
+
? path.resolve(config.templateDir)
|
|
44
|
+
: path.join(__dirname, 'app', 'views');
|
|
45
|
+
const layoutPath = path.join(viewRoot, 'layouts', 'index.hbs');
|
|
14
46
|
|
|
15
|
-
|
|
16
|
-
|
|
47
|
+
subApp.engine('hbs', exphbs({
|
|
48
|
+
extname: 'hbs',
|
|
49
|
+
defaultLayout: layoutPath,
|
|
50
|
+
helpers: {
|
|
51
|
+
theme_color: () => config.theme_color,
|
|
52
|
+
logo: () => config.logo,
|
|
53
|
+
brand: () => config.brand || 'Secure Pay',
|
|
54
|
+
},
|
|
55
|
+
}));
|
|
56
|
+
subApp.set('view engine', 'handlebars');
|
|
17
57
|
|
|
18
|
-
|
|
19
|
-
|
|
58
|
+
// body parsing with raw body capture (needed for webhooks)
|
|
59
|
+
const saveRawBody = (req, res, buf) => {
|
|
60
|
+
req.rawBody = buf.toString();
|
|
61
|
+
};
|
|
62
|
+
subApp.use(bodyParser.urlencoded({ extended: true }));
|
|
63
|
+
subApp.use(bodyParser.json({ verify: saveRawBody }));
|
|
64
|
+
// wire routes against existing payment controller (logic unchanged)
|
|
65
|
+
const callbacks = config.callbacks || userConfig.callbacks;
|
|
66
|
+
const pc = require('./app/controllers/payment_controller')(subApp, callbacks);
|
|
67
|
+
|
|
68
|
+
subApp.all('/init', pc.init);
|
|
69
|
+
subApp.all('/callback', pc.callback);
|
|
70
|
+
subApp.all('/api/webhook', pc.webhook);
|
|
71
|
+
subApp.all('/api/status', pc.status);
|
|
72
|
+
subApp.all('/api/createTxn/token', pc.createTxnToken);
|
|
73
|
+
subApp.all('/api/createTxn', pc.createTxn);
|
|
74
|
+
subApp.all('/', pc.init);
|
|
75
|
+
|
|
76
|
+
subApp.use(express.static(path.join(__dirname, 'public')), pc.init);
|
|
77
|
+
|
|
78
|
+
return subApp;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { createPaymentMiddleware };
|
|
20
82
|
|
|
21
|
-
return module;
|
|
22
|
-
};
|
|
23
83
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const defaults = require('./defaults');
|
|
2
|
+
const { validateConfig } = require('./validator');
|
|
3
|
+
|
|
4
|
+
function pickEnv(keys) {
|
|
5
|
+
const output = {};
|
|
6
|
+
keys.forEach((key) => {
|
|
7
|
+
if (process.env[key] !== undefined) {
|
|
8
|
+
output[key] = process.env[key];
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
return output;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function buildConfig(userConfig = {}) {
|
|
15
|
+
const envOverrides = pickEnv([
|
|
16
|
+
'NP_HOST_URL',
|
|
17
|
+
'NP_PATH_PREFIX',
|
|
18
|
+
'NP_HOMEPAGE',
|
|
19
|
+
'NP_TEMPLATE_DIR',
|
|
20
|
+
'NP_THEME_COLOR',
|
|
21
|
+
'NP_LOGO',
|
|
22
|
+
'NP_DB_URL',
|
|
23
|
+
'NP_PAYTM_URL',
|
|
24
|
+
'NP_RAZOR_URL',
|
|
25
|
+
'NP_PAYU_URL',
|
|
26
|
+
'NP_OPEN_MONEY_URL',
|
|
27
|
+
'NP_MID',
|
|
28
|
+
'NP_WEBSITE',
|
|
29
|
+
'NP_KEY',
|
|
30
|
+
'NP_SECRET',
|
|
31
|
+
'NP_CHANNEL_ID',
|
|
32
|
+
'NP_INDUSTRY_TYPE_ID',
|
|
33
|
+
'NP_MODE',
|
|
34
|
+
'NP_THEME_NAME',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const merged = {
|
|
38
|
+
...defaults,
|
|
39
|
+
...normalizeEnv(envOverrides),
|
|
40
|
+
...userConfig,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// theme normalization
|
|
44
|
+
const themeName = (userConfig.themeName || userConfig.theme?.name || merged.theme_name || 'dark').toLowerCase();
|
|
45
|
+
const theme = {
|
|
46
|
+
primary: getThemeColor(userConfig, merged),
|
|
47
|
+
accent: userConfig?.theme?.accent || merged.theme_accent || '#4ae0ff',
|
|
48
|
+
surface: userConfig?.theme?.surface || '#0f1021',
|
|
49
|
+
text: userConfig?.theme?.text || '#e9ecf2',
|
|
50
|
+
success: userConfig?.theme?.success || '#24cf5f',
|
|
51
|
+
danger: userConfig?.theme?.danger || '#ff6b6b',
|
|
52
|
+
name: themeName,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
merged.theme = theme;
|
|
56
|
+
merged.theme_color = theme.primary;
|
|
57
|
+
merged.logo = userConfig.logo || merged.logo;
|
|
58
|
+
merged.brand = userConfig.brand || 'Secure Pay';
|
|
59
|
+
merged.callbacks = userConfig.callbacks || userConfig.hooks || merged.callbacks || {};
|
|
60
|
+
merged.templateDir = userConfig.templateDir || merged.templateDir;
|
|
61
|
+
merged.themeName = themeName;
|
|
62
|
+
|
|
63
|
+
// ensure view path remains compatible with legacy controllers
|
|
64
|
+
if (!merged.view_path) {
|
|
65
|
+
merged.view_path = '/../views/';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (userConfig.host_url) {
|
|
69
|
+
merged.host_url = userConfig.host_url;
|
|
70
|
+
} else if (process.env.NP_HOST_URL) {
|
|
71
|
+
merged.host_url = process.env.NP_HOST_URL;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return validateConfig(merged);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getThemeColor(userConfig, merged) {
|
|
78
|
+
if (userConfig?.theme?.primary) return userConfig.theme.primary;
|
|
79
|
+
if (userConfig.theme_color) return userConfig.theme_color;
|
|
80
|
+
if (merged.NP_THEME_COLOR) return merged.NP_THEME_COLOR;
|
|
81
|
+
return merged.theme_color;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeEnv(env) {
|
|
85
|
+
const mapping = {
|
|
86
|
+
NP_HOST_URL: 'host_url',
|
|
87
|
+
NP_PATH_PREFIX: 'path_prefix',
|
|
88
|
+
NP_HOMEPAGE: 'homepage',
|
|
89
|
+
NP_TEMPLATE_DIR: 'templateDir',
|
|
90
|
+
NP_THEME_COLOR: 'theme_color',
|
|
91
|
+
NP_LOGO: 'logo',
|
|
92
|
+
NP_DB_URL: 'db_url',
|
|
93
|
+
NP_PAYTM_URL: 'paytm_url',
|
|
94
|
+
NP_RAZOR_URL: 'razor_url',
|
|
95
|
+
NP_PAYU_URL: 'payu_url',
|
|
96
|
+
NP_OPEN_MONEY_URL: 'open_money_url',
|
|
97
|
+
NP_MID: 'MID',
|
|
98
|
+
NP_WEBSITE: 'WEBSITE',
|
|
99
|
+
NP_KEY: 'KEY',
|
|
100
|
+
NP_SECRET: 'SECRET',
|
|
101
|
+
NP_CHANNEL_ID: 'CHANNEL_ID',
|
|
102
|
+
NP_INDUSTRY_TYPE_ID: 'INDUSTRY_TYPE_ID',
|
|
103
|
+
NP_MODE: 'mode',
|
|
104
|
+
NP_THEME_NAME: 'themeName',
|
|
105
|
+
};
|
|
106
|
+
return Object.keys(env).reduce((acc, key) => {
|
|
107
|
+
const target = mapping[key];
|
|
108
|
+
if (target) acc[target] = env[key];
|
|
109
|
+
return acc;
|
|
110
|
+
}, {});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = buildConfig;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
// Server configuration
|
|
3
|
+
host_url: 'http://localhost:3000',
|
|
4
|
+
path_prefix: '_pay',
|
|
5
|
+
homepage: '/',
|
|
6
|
+
|
|
7
|
+
// Template configuration
|
|
8
|
+
templateDir: null, // null = use built-in views, or provide path to custom templates
|
|
9
|
+
templateEngine: 'handlebars',
|
|
10
|
+
|
|
11
|
+
// UI Customization
|
|
12
|
+
theme_color: '#3399cc',
|
|
13
|
+
logo: '/favicon.ico',
|
|
14
|
+
|
|
15
|
+
// Transaction ID configuration
|
|
16
|
+
id_length: 10,
|
|
17
|
+
|
|
18
|
+
// Database
|
|
19
|
+
db_url: null, // MongoDB URL (legacy), leave null to use multidborm
|
|
20
|
+
|
|
21
|
+
// Payment Gateway URLs
|
|
22
|
+
// paytm_url: null, // e.g., 'https://securegw-stage.paytm.in' for test, 'https://securegw.paytm.in' for production
|
|
23
|
+
// razor_url: null, // e.g., 'https://api.razorpay.com/v1/'
|
|
24
|
+
// payu_url: null, // e.g., 'https://test.payu.in' for test, 'https://secure.payu.in' for production
|
|
25
|
+
// open_money_url: null, // e.g., 'https://sandbox-icp-api.bankopen.co/api' for sandbox, 'https://icp-api.bankopen.co/api' for live
|
|
26
|
+
|
|
27
|
+
// Gateway Credentials (must be provided by user)
|
|
28
|
+
// MID: null,
|
|
29
|
+
// WEBSITE: null,
|
|
30
|
+
// KEY: null,
|
|
31
|
+
// SECRET: null,
|
|
32
|
+
// CHANNEL_ID: 'WEB',
|
|
33
|
+
// INDUSTRY_TYPE_ID: 'Retail',
|
|
34
|
+
|
|
35
|
+
// Payment mode configuration (optional)
|
|
36
|
+
// mode: null // JSON string of enabled payment modes for Paytm
|
|
37
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const defaults = require('./defaults');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validates and merges config with defaults
|
|
5
|
+
* @param {Object} userConfig - User-provided configuration
|
|
6
|
+
* @returns {Object} Merged and validated configuration
|
|
7
|
+
* @throws {Error} If required fields are missing
|
|
8
|
+
*/
|
|
9
|
+
function validateConfig(userConfig) {
|
|
10
|
+
if (!userConfig || typeof userConfig !== 'object') {
|
|
11
|
+
throw new Error('Config must be an object');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Merge with defaults
|
|
15
|
+
const config = { ...defaults, ...userConfig };
|
|
16
|
+
|
|
17
|
+
// Detect which payment gateway is being used
|
|
18
|
+
const hasPaytm = !!config.paytm_url;
|
|
19
|
+
const hasRazorpay = !!config.razor_url;
|
|
20
|
+
const hasPayU = !!config.payu_url;
|
|
21
|
+
const hasOpenMoney = !!config.open_money_url;
|
|
22
|
+
|
|
23
|
+
if (!hasPaytm && !hasRazorpay && !hasPayU && !hasOpenMoney) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'At least one payment gateway must be configured. ' +
|
|
26
|
+
'Please provide one of: paytm_url, razor_url, payu_url, or open_money_url'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Validate required credentials based on gateway
|
|
31
|
+
if (hasPaytm) {
|
|
32
|
+
validatePaytmConfig(config);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (hasRazorpay) {
|
|
36
|
+
validateRazorpayConfig(config);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (hasPayU) {
|
|
40
|
+
validatePayUConfig(config);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (hasOpenMoney) {
|
|
44
|
+
validateOpenMoneyConfig(config);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Validate common fields
|
|
48
|
+
if (!config.host_url) {
|
|
49
|
+
throw new Error('host_url is required');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!config.path_prefix) {
|
|
53
|
+
throw new Error('path_prefix is required');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return config;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function validatePaytmConfig(config) {
|
|
60
|
+
const required = ['MID', 'WEBSITE', 'KEY', 'CHANNEL_ID', 'INDUSTRY_TYPE_ID'];
|
|
61
|
+
const missing = required.filter(field => !config[field]);
|
|
62
|
+
|
|
63
|
+
if (missing.length > 0) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Paytm configuration incomplete. Missing fields: ${missing.join(', ')}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function validateRazorpayConfig(config) {
|
|
71
|
+
const required = ['KEY', 'SECRET'];
|
|
72
|
+
const missing = required.filter(field => !config[field]);
|
|
73
|
+
|
|
74
|
+
if (missing.length > 0) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Razorpay configuration incomplete. Missing fields: ${missing.join(', ')}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function validatePayUConfig(config) {
|
|
82
|
+
const required = ['KEY', 'SECRET'];
|
|
83
|
+
const missing = required.filter(field => !config[field]);
|
|
84
|
+
|
|
85
|
+
if (missing.length > 0) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`PayU configuration incomplete. Missing fields: ${missing.join(', ')}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function validateOpenMoneyConfig(config) {
|
|
93
|
+
const required = ['KEY', 'SECRET'];
|
|
94
|
+
const missing = required.filter(field => !config[field]);
|
|
95
|
+
|
|
96
|
+
if (missing.length > 0) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`OpenMoney configuration incomplete. Missing fields: ${missing.join(', ')}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = { validateConfig };
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Service
|
|
3
|
+
* Abstracts database operations to support both MongoDB (legacy) and MultiDB ORM
|
|
4
|
+
*/
|
|
5
|
+
class DatabaseService {
|
|
6
|
+
constructor(db, config) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.usingMultiDbOrm = this.detectDbType();
|
|
10
|
+
|
|
11
|
+
// Will be initialized later
|
|
12
|
+
this.Transaction = null;
|
|
13
|
+
this.User = null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Detects whether we're using MultiDB ORM or MongoDB
|
|
18
|
+
*/
|
|
19
|
+
detectDbType() {
|
|
20
|
+
// Using MultiDB ORM if db instance provided and no db_url in config
|
|
21
|
+
return !!this.db && !this.config.db_url;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Initializes transaction model
|
|
26
|
+
*/
|
|
27
|
+
initTransactionModel() {
|
|
28
|
+
if (this.config.db_url) {
|
|
29
|
+
// Legacy MongoDB mode
|
|
30
|
+
this.Transaction = require('../models/transaction.model');
|
|
31
|
+
} else if (this.db) {
|
|
32
|
+
// MultiDB ORM mode
|
|
33
|
+
const sample = {
|
|
34
|
+
orderId: "string",
|
|
35
|
+
cusId: "string",
|
|
36
|
+
time: 1770051201752,
|
|
37
|
+
timeStamp: 1770051201752,
|
|
38
|
+
status: "string",
|
|
39
|
+
name: "string",
|
|
40
|
+
email: "string",
|
|
41
|
+
phone: "string",
|
|
42
|
+
amount: 1,
|
|
43
|
+
pname: "string",
|
|
44
|
+
extra: "stringlarge",
|
|
45
|
+
TXNID: "27118670199",
|
|
46
|
+
returnUrl: "string"
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const multiDbPlugin = require('../models/np_multidbplugin');
|
|
50
|
+
this.Transaction = multiDbPlugin('nptransactions', this.db, sample);
|
|
51
|
+
this.Transaction.db = this.db;
|
|
52
|
+
this.Transaction.modelname = 'nptransactions';
|
|
53
|
+
this.Transaction.idFieldName = 'orderId';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return this.Transaction;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Initializes user model
|
|
61
|
+
*/
|
|
62
|
+
initUserModel() {
|
|
63
|
+
if (this.config.db_url) {
|
|
64
|
+
// Legacy MongoDB mode
|
|
65
|
+
this.User = require('../models/user.model');
|
|
66
|
+
} else if (this.db) {
|
|
67
|
+
// MultiDB ORM mode
|
|
68
|
+
const sample = {
|
|
69
|
+
id: "string",
|
|
70
|
+
name: "string",
|
|
71
|
+
email: "string",
|
|
72
|
+
phone: "string"
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const multiDbPlugin = require('../models/np_multidbplugin');
|
|
76
|
+
this.User = multiDbPlugin('npusers', this.db, sample);
|
|
77
|
+
this.User.db = this.db;
|
|
78
|
+
this.User.modelname = 'npusers';
|
|
79
|
+
this.User.idFieldName = 'id';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return this.User;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Finds one document
|
|
87
|
+
* @param {Object} model - Mongoose model or MultiDB model
|
|
88
|
+
* @param {Object} query - Query object
|
|
89
|
+
* @param {Function} callback - Callback function (err, doc)
|
|
90
|
+
*/
|
|
91
|
+
findOne(model, query, callback) {
|
|
92
|
+
if (this.usingMultiDbOrm) {
|
|
93
|
+
model.findOne(query, callback, model);
|
|
94
|
+
} else {
|
|
95
|
+
model.findOne(query, callback);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Creates and saves a new document
|
|
101
|
+
* @param {Object} modelClass - Model constructor
|
|
102
|
+
* @param {Object} data - Data to save
|
|
103
|
+
* @returns {Promise} Promise that resolves with saved document
|
|
104
|
+
*/
|
|
105
|
+
create(modelClass, data) {
|
|
106
|
+
const doc = new modelClass(data);
|
|
107
|
+
return doc.save();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Updates one document
|
|
112
|
+
* @param {Object} model - Mongoose model or MultiDB model
|
|
113
|
+
* @param {Object} query - Query object
|
|
114
|
+
* @param {Object} update - Update object
|
|
115
|
+
* @param {Function} callback - Callback function (err, result)
|
|
116
|
+
*/
|
|
117
|
+
updateOne(model, query, update, callback) {
|
|
118
|
+
if (this.usingMultiDbOrm) {
|
|
119
|
+
model.updateOne(query, update, callback);
|
|
120
|
+
} else {
|
|
121
|
+
model.updateOne(query, update, callback);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Gets the Transaction model
|
|
127
|
+
*/
|
|
128
|
+
getTransactionModel() {
|
|
129
|
+
if (!this.Transaction) {
|
|
130
|
+
this.initTransactionModel();
|
|
131
|
+
}
|
|
132
|
+
return this.Transaction;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Gets the User model
|
|
137
|
+
*/
|
|
138
|
+
getUserModel() {
|
|
139
|
+
if (!this.User) {
|
|
140
|
+
this.initUserModel();
|
|
141
|
+
}
|
|
142
|
+
return this.User;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Returns whether using MultiDB ORM
|
|
147
|
+
*/
|
|
148
|
+
isUsingMultiDbOrm() {
|
|
149
|
+
return this.usingMultiDbOrm;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = DatabaseService;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a random alphanumeric ID
|
|
3
|
+
* @param {number} length - Length of the ID to generate
|
|
4
|
+
* @returns {string} Random ID
|
|
5
|
+
*/
|
|
6
|
+
function generateId(length = 10) {
|
|
7
|
+
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
8
|
+
let text = "";
|
|
9
|
+
|
|
10
|
+
for (let i = 0; i < length; i++) {
|
|
11
|
+
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return text;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generates an order ID with a prefix
|
|
19
|
+
* @param {string} prefix - Prefix for the order ID (e.g., 'pay_', 'payu_')
|
|
20
|
+
* @param {number} length - Length of the random part
|
|
21
|
+
* @returns {string} Order ID
|
|
22
|
+
*/
|
|
23
|
+
function generateOrderId(prefix = '', length = 10) {
|
|
24
|
+
return prefix + generateId(length);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
generateId,
|
|
29
|
+
generateOrderId
|
|
30
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitizes request body by parsing amount fields to floats
|
|
3
|
+
* @param {Object} body - Request body to sanitize
|
|
4
|
+
* @returns {Object} Sanitized request body
|
|
5
|
+
*/
|
|
6
|
+
function sanitizeRequest(body) {
|
|
7
|
+
if (!body || typeof body !== 'object') {
|
|
8
|
+
return body;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Parse amount to float if present
|
|
12
|
+
if (body.amount) {
|
|
13
|
+
body.amount = parseFloat(body.amount);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (body.TXN_AMOUNT) {
|
|
17
|
+
body.TXN_AMOUNT = parseFloat(body.TXN_AMOUNT);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return body;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
sanitizeRequest
|
|
25
|
+
};
|