node-paytmpg 7.5.17 → 7.5.19
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/dist/app/controllers/adapters/open_money.d.ts +23 -0
- package/dist/app/controllers/adapters/open_money.js +399 -0
- package/dist/app/controllers/adapters/paytm.d.ts +2 -0
- package/dist/app/controllers/adapters/paytm.js +34 -0
- package/dist/app/controllers/adapters/payu.d.ts +50 -0
- package/dist/app/controllers/adapters/payu.js +238 -0
- package/dist/app/controllers/checksum/PaytmChecksum.d.ts +13 -0
- package/dist/app/controllers/checksum/PaytmChecksum.js +118 -0
- package/dist/app/controllers/checksum/checksum.d.ts +15 -0
- package/dist/app/controllers/checksum/checksum.js +158 -0
- package/dist/app/controllers/checksum/crypt.d.ts +15 -0
- package/dist/app/controllers/checksum/crypt.js +117 -0
- package/dist/app/controllers/checksum/server.d.ts +1 -0
- package/dist/app/controllers/checksum/server.js +132 -0
- package/dist/app/controllers/htmlhelper.d.ts +9 -0
- package/dist/app/controllers/htmlhelper.js +95 -0
- package/dist/app/controllers/payment.controller.d.ts +33 -0
- package/dist/app/controllers/payment.controller.js +1128 -0
- package/dist/app/controllers/static/loadingsvg.d.ts +1 -0
- package/dist/app/controllers/static/loadingsvg.js +54 -0
- package/dist/app/controllers/user.controller.d.ts +9 -0
- package/dist/app/controllers/user.controller.js +53 -0
- package/dist/app/models/index.d.ts +103 -0
- package/dist/app/models/index.js +2 -0
- package/dist/app/routes/payment_route.d.ts +2 -0
- package/dist/app/routes/payment_route.js +46 -0
- package/dist/app/utils/buildConfig.d.ts +4 -0
- package/dist/app/utils/buildConfig.js +224 -0
- package/dist/app/utils/utils.d.ts +5 -0
- package/dist/app/utils/utils.js +20 -0
- package/dist/app/views/home.hbs +22 -0
- package/dist/app/views/init.hbs +104 -0
- package/dist/app/views/layouts/index.hbs +53 -0
- package/dist/app/views/result.hbs +33 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +120 -0
- package/dist/package.json +68 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.PaymentController = void 0;
|
|
40
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
41
|
+
const checksum_1 = __importDefault(require("./checksum/checksum"));
|
|
42
|
+
const PaytmChecksum_1 = __importDefault(require("./checksum/PaytmChecksum"));
|
|
43
|
+
const crypto = __importStar(require("crypto"));
|
|
44
|
+
const path_1 = __importDefault(require("path"));
|
|
45
|
+
const axios_1 = __importDefault(require("axios"));
|
|
46
|
+
const razorpay_1 = __importDefault(require("razorpay"));
|
|
47
|
+
const open_money_1 = __importDefault(require("./adapters/open_money"));
|
|
48
|
+
const payu_1 = __importDefault(require("./adapters/payu"));
|
|
49
|
+
const user_controller_1 = require("./user.controller");
|
|
50
|
+
const utils_1 = require("../utils/utils");
|
|
51
|
+
const loadingsvg_1 = require("./static/loadingsvg");
|
|
52
|
+
const htmlhelper_1 = require("./htmlhelper");
|
|
53
|
+
const buildConfig_1 = require("../utils/buildConfig");
|
|
54
|
+
const IDLEN = 14;
|
|
55
|
+
function makeid(length) {
|
|
56
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
57
|
+
let result = '';
|
|
58
|
+
for (let i = 0; i < length; i++) {
|
|
59
|
+
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
class PaymentController {
|
|
64
|
+
constructor(baseConfig, db, callbacks, tableNames) {
|
|
65
|
+
this.tableNames = { USER: 'npusers', TRANSACTION: 'nptransactions' };
|
|
66
|
+
this.viewPath = '';
|
|
67
|
+
this.baseConfig = baseConfig;
|
|
68
|
+
this.callbacks = callbacks;
|
|
69
|
+
this.db = db;
|
|
70
|
+
if (tableNames) {
|
|
71
|
+
this.tableNames = tableNames;
|
|
72
|
+
}
|
|
73
|
+
this.useController = new user_controller_1.NPUserController(this.db, this.tableNames.USER);
|
|
74
|
+
this.configure(baseConfig);
|
|
75
|
+
}
|
|
76
|
+
encodeTxnDataForUrl(txnDataJson, req) {
|
|
77
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
78
|
+
// Accept either an object or a JSON string.
|
|
79
|
+
const payloadStr = typeof txnDataJson === 'string' ? txnDataJson : JSON.stringify(txnDataJson);
|
|
80
|
+
// Derive a 32-byte key from config.SECRET (fallback to config.KEY).
|
|
81
|
+
const secret = String(config.SECRET || config.KEY || '');
|
|
82
|
+
if (!secret) {
|
|
83
|
+
// No secret available — fallback to url-safe base64 (not secure).
|
|
84
|
+
return Buffer.from(payloadStr, 'utf8').toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
|
85
|
+
}
|
|
86
|
+
const key = crypto.createHash('sha256').update(secret).digest(); // 32 bytes
|
|
87
|
+
const iv = crypto.randomBytes(12); // 12 bytes recommended for GCM
|
|
88
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
89
|
+
const encrypted = Buffer.concat([cipher.update(payloadStr, 'utf8'), cipher.final()]);
|
|
90
|
+
const tag = cipher.getAuthTag();
|
|
91
|
+
// Store as: iv (12) | tag (16) | ciphertext — then URL-safe base64
|
|
92
|
+
const out = Buffer.concat([iv, tag, encrypted]).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
decodeTxnDataFromUrl(encodedStr, req) {
|
|
96
|
+
if (!encodedStr)
|
|
97
|
+
return '';
|
|
98
|
+
// Convert back to standard base64 and pad
|
|
99
|
+
let b64 = encodedStr.replace(/-/g, '+').replace(/_/g, '/');
|
|
100
|
+
const pad = b64.length % 4;
|
|
101
|
+
if (pad)
|
|
102
|
+
b64 += '='.repeat(4 - pad);
|
|
103
|
+
const raw = Buffer.from(b64, 'base64');
|
|
104
|
+
// If too short to contain iv+tag, treat as plain base64 payload
|
|
105
|
+
if (raw.length < 12 + 16 + 1) {
|
|
106
|
+
try {
|
|
107
|
+
return raw.toString('utf8');
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
return '';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const iv = raw.slice(0, 12);
|
|
115
|
+
const tag = raw.slice(12, 28);
|
|
116
|
+
const ciphertext = raw.slice(28);
|
|
117
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
118
|
+
const secret = String(config.SECRET || config.KEY || '');
|
|
119
|
+
if (!secret)
|
|
120
|
+
return raw.toString('utf8');
|
|
121
|
+
const key = crypto.createHash('sha256').update(secret).digest();
|
|
122
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
123
|
+
decipher.setAuthTag(tag);
|
|
124
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8');
|
|
125
|
+
return decrypted;
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
// Fallback: return plain base64-decoded string (best-effort)
|
|
129
|
+
try {
|
|
130
|
+
return Buffer.from(b64, 'base64').toString('utf8');
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
console.log('decodeTxnDataFromUrl error', e);
|
|
134
|
+
return '';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
getProviderInstance(providerName, config) {
|
|
139
|
+
switch (providerName.toLocaleLowerCase()) {
|
|
140
|
+
case 'PayU'.toLocaleLowerCase():
|
|
141
|
+
return new payu_1.default(config);
|
|
142
|
+
case 'OpenMoney'.toLocaleLowerCase():
|
|
143
|
+
return new open_money_1.default(config);
|
|
144
|
+
case 'RazorPay'.toLocaleLowerCase():
|
|
145
|
+
return new razorpay_1.default({ key_id: config.KEY, key_secret: config.SECRET });
|
|
146
|
+
default:
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
configure(config) {
|
|
151
|
+
const viewRoot = config.templateDir
|
|
152
|
+
? config.templateDir
|
|
153
|
+
: path_1.default.join(__dirname, '..', 'views');
|
|
154
|
+
this.viewPath = viewRoot.endsWith(path_1.default.sep) ? viewRoot : viewRoot + path_1.default.sep;
|
|
155
|
+
const sample = {
|
|
156
|
+
orderId: "string",
|
|
157
|
+
cusId: "string",
|
|
158
|
+
time: 1770051201752,
|
|
159
|
+
timeStamp: 1770051201752,
|
|
160
|
+
status: "string",
|
|
161
|
+
name: "string",
|
|
162
|
+
email: "string",
|
|
163
|
+
phone: "12345678",
|
|
164
|
+
amount: 1,
|
|
165
|
+
pname: "string",
|
|
166
|
+
extra: "stringlarge",
|
|
167
|
+
TXNID: "27118670199",
|
|
168
|
+
returnUrl: "string"
|
|
169
|
+
};
|
|
170
|
+
this.db.create(this.tableNames.TRANSACTION, sample).catch(() => { });
|
|
171
|
+
}
|
|
172
|
+
async insertTransactionInDb(txnData) {
|
|
173
|
+
await this.db.insert(this.tableNames.TRANSACTION, txnData);
|
|
174
|
+
return txnData;
|
|
175
|
+
}
|
|
176
|
+
async generateChecksum(params, req) {
|
|
177
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
178
|
+
return await new Promise((resolve, reject) => {
|
|
179
|
+
checksum_1.default.genchecksum(params, config.KEY, (err, cs) => {
|
|
180
|
+
if (err || !cs) {
|
|
181
|
+
reject(err || new Error('Error generating checksum'));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
resolve(cs);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
home(req, res) {
|
|
189
|
+
package_json_1.default.repository.url = package_json_1.default.repository.url.replace('git+', '');
|
|
190
|
+
return (0, htmlhelper_1.renderView)(req, res, this.viewPath + "home.hbs", package_json_1.default);
|
|
191
|
+
}
|
|
192
|
+
async init(req, res) {
|
|
193
|
+
var _a;
|
|
194
|
+
const callbacks = this.callbacks;
|
|
195
|
+
const vp = this.viewPath;
|
|
196
|
+
if (!req.body.ORDER_ID && !req.body.EMAIL && ((_a = req.query) === null || _a === void 0 ? void 0 : _a.to)) {
|
|
197
|
+
let toData = {};
|
|
198
|
+
try {
|
|
199
|
+
toData = JSON.parse(this.decodeTxnDataFromUrl(req.query.to, req));
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
console.log("Error parsing 'to' query param data");
|
|
203
|
+
}
|
|
204
|
+
req.body.NAME = toData.NAME;
|
|
205
|
+
req.body.EMAIL = toData.EMAIL;
|
|
206
|
+
req.body.TXN_AMOUNT = toData.TXN_AMOUNT;
|
|
207
|
+
req.body.MOBILE_NO = toData.MOBILE_NO;
|
|
208
|
+
req.body.ORDER_ID = toData.ORDER_ID || toData.ORDERID;
|
|
209
|
+
req.body.PRODUCT_NAME = toData.PRODUCT_NAME;
|
|
210
|
+
req.body.RETURN_URL = toData.RETURN_URL;
|
|
211
|
+
req.body.CLIENT_ID = toData.CLIENT_ID;
|
|
212
|
+
req.body.STATE = toData.STATE;
|
|
213
|
+
}
|
|
214
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
215
|
+
utils_1.Utils.sanitizeRequest(req.body);
|
|
216
|
+
let gotAllParams = true;
|
|
217
|
+
let checkedFields = ['TXN_AMOUNT', 'PRODUCT_NAME',
|
|
218
|
+
// 'MOBILE_NO',
|
|
219
|
+
'NAME', 'EMAIL'];
|
|
220
|
+
if (req.body !== undefined) {
|
|
221
|
+
for (var i = 0; i < checkedFields.length; i++) {
|
|
222
|
+
if (req.body[checkedFields[i]] === undefined) {
|
|
223
|
+
gotAllParams = false;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
gotAllParams = false;
|
|
230
|
+
}
|
|
231
|
+
// console.log(req.body)
|
|
232
|
+
if ((req.body.ORDER_ID !== undefined && req.body.ORDER_ID.length > 2)
|
|
233
|
+
&&
|
|
234
|
+
(req.body.CUST_ID !== undefined && req.body.CUST_ID.length > 2)) {
|
|
235
|
+
// console.log('redirect')
|
|
236
|
+
// console.log(req.body)
|
|
237
|
+
var params = {
|
|
238
|
+
TXN_AMOUNT: req.body.TXN_AMOUNT,
|
|
239
|
+
};
|
|
240
|
+
params['MID'] = req.body.MID;
|
|
241
|
+
params['WEBSITE'] = req.body.WEBSITE;
|
|
242
|
+
params['CHANNEL_ID'] = req.body.CHANNEL_ID;
|
|
243
|
+
params['INDUSTRY_TYPE_ID'] = req.body.INDUSTRY_TYPE_ID;
|
|
244
|
+
params['ORDER_ID'] = req.body.ORDER_ID || req.body.ORDERID;
|
|
245
|
+
params['CUST_ID'] = req.body.CUST_ID;
|
|
246
|
+
params['TXN_AMOUNT'] = req.body.TXN_AMOUNT;
|
|
247
|
+
params['CALLBACK_URL'] = req.body.CALLBACK_URL + "?order_id=" + req.body.ORDER_ID;
|
|
248
|
+
params['EMAIL'] = req.body.EMAIL;
|
|
249
|
+
params['MOBILE_NO'] = req.body.MOBILE_NO;
|
|
250
|
+
params['PRODUCT_NAME'] = req.body.PRODUCT_NAME;
|
|
251
|
+
params['NAME'] = req.body.NAME;
|
|
252
|
+
if (config.paytm_url) {
|
|
253
|
+
let initTxnbody = {
|
|
254
|
+
"requestType": "Payment",
|
|
255
|
+
"mid": params['MID'],
|
|
256
|
+
"websiteName": params['WEBSITE'],
|
|
257
|
+
"orderId": params['ORDER_ID'],
|
|
258
|
+
"callbackUrl": params['CALLBACK_URL'],
|
|
259
|
+
"txnAmount": {
|
|
260
|
+
"value": params['TXN_AMOUNT'],
|
|
261
|
+
"currency": params['CURRENCY'] || "INR",
|
|
262
|
+
},
|
|
263
|
+
"userInfo": {
|
|
264
|
+
"custId": params['CUST_ID'],
|
|
265
|
+
"mobile": params['MOBILE_NO'],
|
|
266
|
+
"firstName": params['NAME'],
|
|
267
|
+
"email": params['EMAIL']
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
if (config.mode) {
|
|
271
|
+
initTxnbody["enablePaymentMode"] = JSON.parse(config.mode);
|
|
272
|
+
}
|
|
273
|
+
let checksum = await PaytmChecksum_1.default.generateSignature(JSON.stringify(initTxnbody), config.KEY);
|
|
274
|
+
let initTxnUrl = config.paytm_url + `/theia/api/v1/initiateTransaction?mid=${params['MID']}&orderId=${params['ORDER_ID']}`;
|
|
275
|
+
try {
|
|
276
|
+
const resp = await axios_1.default.post(initTxnUrl, {
|
|
277
|
+
body: initTxnbody,
|
|
278
|
+
head: {
|
|
279
|
+
signature: checksum,
|
|
280
|
+
channelId: params['CHANNEL_ID']
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
const body = resp.data;
|
|
284
|
+
if (resp.status === 200 && body && body.body && body.body.resultInfo && body.body.resultInfo.resultStatus === 'S') {
|
|
285
|
+
let paytmJsToken = {};
|
|
286
|
+
paytmJsToken.CALLBACK_URL = params['CALLBACK_URL'];
|
|
287
|
+
paytmJsToken.ORDERID = params['ORDER_ID'];
|
|
288
|
+
paytmJsToken.ORDER_ID = params['ORDER_ID'];
|
|
289
|
+
paytmJsToken.CANCELLED = 'cancelled';
|
|
290
|
+
paytmJsToken.TOKEN = body.body.txnToken;
|
|
291
|
+
paytmJsToken.TXN_AMOUNT = params['TXN_AMOUNT'];
|
|
292
|
+
paytmJsToken.MID = params['MID'];
|
|
293
|
+
paytmJsToken.CALLBACK_URL = params['CALLBACK_URL'];
|
|
294
|
+
return (0, htmlhelper_1.renderPaytmJsCheckout)(req, res, paytmJsToken, config);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
console.log('ERROR:::', resp.status, '\n', body);
|
|
298
|
+
res.status(500);
|
|
299
|
+
const errorResp = {
|
|
300
|
+
TXNID: 'na',
|
|
301
|
+
STATUS: 'TXN_FAILURE',
|
|
302
|
+
CANCELLED: 'cancelled',
|
|
303
|
+
ORDERID: params['ORDER_ID'],
|
|
304
|
+
CHECKSUMHASH: checksum
|
|
305
|
+
};
|
|
306
|
+
return (0, htmlhelper_1.sendAutoPostForm)(req, res, params['CALLBACK_URL'], errorResp);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
console.log('ERROR:::', err);
|
|
311
|
+
res.status(500);
|
|
312
|
+
const errorResp = {
|
|
313
|
+
TXNID: 'na',
|
|
314
|
+
STATUS: 'TXN_FAILURE',
|
|
315
|
+
CANCELLED: 'cancelled',
|
|
316
|
+
ORDERID: params['ORDER_ID'],
|
|
317
|
+
CHECKSUMHASH: checksum
|
|
318
|
+
};
|
|
319
|
+
return (0, htmlhelper_1.sendAutoPostForm)(req, res, params['CALLBACK_URL'], errorResp);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else if (config.razor_url) {
|
|
323
|
+
return (0, htmlhelper_1.renderRazorpayCheckout)(req, res, params, config, loadingsvg_1.LoadingSVG);
|
|
324
|
+
}
|
|
325
|
+
else if (config.payu_url) {
|
|
326
|
+
const payuInstance = this.getProviderInstance('PayU', config);
|
|
327
|
+
const payuRequest = payuInstance.generatePaymentRequest(params);
|
|
328
|
+
payuInstance.renderProcessingPage(params, payuRequest, res, loadingsvg_1.LoadingSVG);
|
|
329
|
+
}
|
|
330
|
+
else if (config.open_money_url) {
|
|
331
|
+
const openMoneyInstance = this.getProviderInstance('OpenMoney', config);
|
|
332
|
+
try {
|
|
333
|
+
let pmttoken = await openMoneyInstance.generatePaymentToken(params);
|
|
334
|
+
openMoneyInstance.renderProcessingPage(params, pmttoken, res, loadingsvg_1.LoadingSVG);
|
|
335
|
+
var myquery = { orderId: params['ORDER_ID'] };
|
|
336
|
+
const objForUpdate = await this.db.getOne(this.tableNames.TRANSACTION, myquery);
|
|
337
|
+
if (objForUpdate) {
|
|
338
|
+
objForUpdate.extra = JSON.stringify({
|
|
339
|
+
layer_pay_token_id: pmttoken.tokenid
|
|
340
|
+
});
|
|
341
|
+
await this.db.update(this.tableNames.TRANSACTION, myquery, objForUpdate);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (e) {
|
|
345
|
+
openMoneyInstance.renderError(params, e, res);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (this.callbacks && typeof this.callbacks.onStart === 'function') {
|
|
349
|
+
this.callbacks.onStart(params['ORDER_ID'], params);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
else if ((req.body.ORDER_ID !== undefined && req.body.ORDER_ID.length > 2) || gotAllParams) {
|
|
353
|
+
let user = await this.useController.create({ name: req.body.NAME, email: req.body.EMAIL, phone: req.body.MOBILE_NO });
|
|
354
|
+
//console.log(user)
|
|
355
|
+
const onTxn = async (txnData) => {
|
|
356
|
+
//console.log(txnData)
|
|
357
|
+
const params = {};
|
|
358
|
+
params['MID'] = config.MID;
|
|
359
|
+
params['WEBSITE'] = config.WEBSITE;
|
|
360
|
+
params['CHANNEL_ID'] = config.CHANNEL_ID;
|
|
361
|
+
params['INDUSTRY_TYPE_ID'] = config.INDUSTRY_TYPE_ID;
|
|
362
|
+
params['ORDER_ID'] = txnData.orderId;
|
|
363
|
+
params['CUST_ID'] = txnData.cusId;
|
|
364
|
+
params['TXN_AMOUNT'] = JSON.stringify(txnData.amount);
|
|
365
|
+
params['CALLBACK_URL'] = config.host_url + '/' + config.path_prefix + '/callback';
|
|
366
|
+
params['EMAIL'] = txnData.email;
|
|
367
|
+
params['MOBILE_NO'] = txnData.phone;
|
|
368
|
+
params['NAME'] = txnData.name;
|
|
369
|
+
params['PRODUCT_NAME'] = txnData.pname;
|
|
370
|
+
const showConfirmation = (checksum = '') => {
|
|
371
|
+
return (0, htmlhelper_1.renderView)(req, res, vp + "init.hbs", {
|
|
372
|
+
name: 'ASDASD',
|
|
373
|
+
enableConfirmationPage: config.enableConfirmationPage,
|
|
374
|
+
theme: config.theme,
|
|
375
|
+
path_prefix: config.path_prefix,
|
|
376
|
+
action: "/" + config.path_prefix + "/init",
|
|
377
|
+
readonly: 'readonly',
|
|
378
|
+
BUTTON: 'Pay',
|
|
379
|
+
NAME: params['NAME'],
|
|
380
|
+
EMAIL: params['EMAIL'],
|
|
381
|
+
MOBILE_NO: params['MOBILE_NO'],
|
|
382
|
+
PRODUCT_NAME: params['PRODUCT_NAME'],
|
|
383
|
+
TXN_AMOUNT: params['TXN_AMOUNT'],
|
|
384
|
+
MID: params['MID'],
|
|
385
|
+
WEBSITE: params['WEBSITE'],
|
|
386
|
+
ORDER_ID: params['ORDER_ID'],
|
|
387
|
+
CUST_ID: params['CUST_ID'],
|
|
388
|
+
INDUSTRY_TYPE_ID: params['INDUSTRY_TYPE_ID'],
|
|
389
|
+
CHANNEL_ID: params['CHANNEL_ID'],
|
|
390
|
+
CALLBACK_URL: params['CALLBACK_URL'],
|
|
391
|
+
STATE: params['STATE'],
|
|
392
|
+
CHECKSUMHASH: checksum,
|
|
393
|
+
CLIENT_ID: params['CLIENT_ID'] || txnData.clientId || config.client_id || ''
|
|
394
|
+
});
|
|
395
|
+
};
|
|
396
|
+
if (config.paytm_url) {
|
|
397
|
+
const checksum = await this.generateChecksum(params, req);
|
|
398
|
+
showConfirmation(checksum);
|
|
399
|
+
}
|
|
400
|
+
else if (config.razor_url || config.payu_url || config.open_money_url) {
|
|
401
|
+
showConfirmation();
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
const onOrder = async (orderId) => {
|
|
405
|
+
const txnTask = {
|
|
406
|
+
id: orderId,
|
|
407
|
+
orderId: orderId,
|
|
408
|
+
cusId: user.id,
|
|
409
|
+
time: Date.now(),
|
|
410
|
+
timeStamp: Date.now(),
|
|
411
|
+
status: 'INITIATED',
|
|
412
|
+
name: user.name,
|
|
413
|
+
email: user.email,
|
|
414
|
+
phone: user.phone,
|
|
415
|
+
amount: req.body.TXN_AMOUNT,
|
|
416
|
+
pname: req.body.PRODUCT_NAME,
|
|
417
|
+
extra: '',
|
|
418
|
+
returnUrl: req.body.RETURN_URL || '',
|
|
419
|
+
webhookUrl: req.body.WEBHOOK_URL || '',
|
|
420
|
+
state: req.body.STATE || '',
|
|
421
|
+
clientId: req.body.CLIENT_ID || ''
|
|
422
|
+
};
|
|
423
|
+
try {
|
|
424
|
+
const txn = await this.insertTransactionInDb(txnTask);
|
|
425
|
+
await onTxn(txn);
|
|
426
|
+
}
|
|
427
|
+
catch (err) {
|
|
428
|
+
console.log(err);
|
|
429
|
+
if (req.body.RETURN_URL) {
|
|
430
|
+
res.redirect(req.body.RETURN_URL + "?status=failed");
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
res.redirect('');
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
if ((req.body.ORDER_ID !== undefined && req.body.ORDER_ID.length > 2)) {
|
|
437
|
+
const myquery = { orderId: req.body.ORDER_ID };
|
|
438
|
+
const orderData = await this.db.getOne(this.tableNames.TRANSACTION, myquery).catch(() => null);
|
|
439
|
+
if (!orderData) {
|
|
440
|
+
if (gotAllParams) {
|
|
441
|
+
console.log("Creating new order for ", req.body.ORDER_ID);
|
|
442
|
+
await onOrder(req.body.ORDER_ID);
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
res.send({ message: "Order Not Found or missing required data: " + checkedFields.join(", "), ORDERID: req.body.ORDER_ID });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
await onTxn(orderData);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
let orderId;
|
|
454
|
+
if (config.paytm_url) {
|
|
455
|
+
orderId = "pay_" + makeid(config.id_length || IDLEN);
|
|
456
|
+
await onOrder(orderId);
|
|
457
|
+
}
|
|
458
|
+
else if (config.razor_url) {
|
|
459
|
+
const options = {
|
|
460
|
+
amount: req.body.TXN_AMOUNT * 100,
|
|
461
|
+
currency: "INR",
|
|
462
|
+
receipt: user.id + '_' + Date.now()
|
|
463
|
+
};
|
|
464
|
+
try {
|
|
465
|
+
const razorPayInstance = this.getProviderInstance('RazorPay', config);
|
|
466
|
+
const order = await razorPayInstance.orders.create(options);
|
|
467
|
+
orderId = order.id;
|
|
468
|
+
await onOrder(orderId);
|
|
469
|
+
}
|
|
470
|
+
catch (err) {
|
|
471
|
+
res.send({ message: "An error occurred ! " + ((err === null || err === void 0 ? void 0 : err.description) || (err === null || err === void 0 ? void 0 : err.message) || 'unknown_error') });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
else if (config.open_money_url) {
|
|
475
|
+
orderId = "pay_" + makeid(config.id_length || IDLEN);
|
|
476
|
+
await onOrder(orderId);
|
|
477
|
+
}
|
|
478
|
+
else if (config.payu_url) {
|
|
479
|
+
orderId = "payu_" + makeid(config.id_length || IDLEN);
|
|
480
|
+
await onOrder(orderId);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
return (0, htmlhelper_1.renderView)(req, res, this.viewPath + "init.hbs", {
|
|
486
|
+
name: 'ASDASD',
|
|
487
|
+
enableConfirmationPage: config.enableConfirmationPage,
|
|
488
|
+
theme: config.theme,
|
|
489
|
+
path_prefix: config.path_prefix,
|
|
490
|
+
action: "/" + config.path_prefix + "/init",
|
|
491
|
+
readonly: '',
|
|
492
|
+
check: true,
|
|
493
|
+
BUTTON: 'Submit',
|
|
494
|
+
NAME: (req.body.NAME === undefined ? '' : req.body.NAME),
|
|
495
|
+
EMAIL: (req.body.EMAIL === undefined ? '' : req.body.EMAIL),
|
|
496
|
+
MOBILE_NO: (req.body.MOBILE_NO === undefined ? '' : req.body.MOBILE_NO),
|
|
497
|
+
PRODUCT_NAME: (req.body.PRODUCT_NAME === undefined ? '' : req.body.PRODUCT_NAME),
|
|
498
|
+
TXN_AMOUNT: (req.body.TXN_AMOUNT === undefined ? '' : req.body.TXN_AMOUNT),
|
|
499
|
+
MID: config.MID,
|
|
500
|
+
WEBSITE: config.WEBSITE,
|
|
501
|
+
ORDER_ID: '',
|
|
502
|
+
CUST_ID: '',
|
|
503
|
+
INDUSTRY_TYPE_ID: config.INDUSTRY_TYPE_ID,
|
|
504
|
+
CHANNEL_ID: config.CHANNEL_ID,
|
|
505
|
+
CALLBACK_URL: config.CALLBACK_URL,
|
|
506
|
+
CHECKSUMHASH: '',
|
|
507
|
+
STATE: (req.body.STATE === undefined ? '' : req.body.STATE),
|
|
508
|
+
CLIENT_ID: config.client_id || req.body.CLIENT_ID || ''
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async updateTransaction(req, res, fromWebhook = false) {
|
|
513
|
+
const vp = this.viewPath;
|
|
514
|
+
const callbacks = this.callbacks;
|
|
515
|
+
const orderToFind = req.body.ORDERID || req.body.ORDER_ID || req.body.ORDERId || (req.query && req.query.order_id) || req.body.ORDER_ID;
|
|
516
|
+
const myquery = { orderId: orderToFind };
|
|
517
|
+
let objForUpdate = null;
|
|
518
|
+
try {
|
|
519
|
+
objForUpdate = await this.db.getOne(this.tableNames.TRANSACTION, myquery).catch(() => null);
|
|
520
|
+
if (!objForUpdate)
|
|
521
|
+
objForUpdate = await this.db.getOne(this.tableNames.TRANSACTION, { id: orderToFind }).catch(() => null);
|
|
522
|
+
if (!objForUpdate)
|
|
523
|
+
objForUpdate = await this.db.getOne(this.tableNames.TRANSACTION, { ORDERID: orderToFind }).catch(() => null);
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
objForUpdate = objForUpdate || null;
|
|
527
|
+
}
|
|
528
|
+
let returnUrl = objForUpdate ? objForUpdate.returnUrl : null;
|
|
529
|
+
let webhookUrl = objForUpdate ? objForUpdate.webhookUrl : null;
|
|
530
|
+
if (webhookUrl === 'undefined')
|
|
531
|
+
webhookUrl = null;
|
|
532
|
+
if (returnUrl === 'undefined')
|
|
533
|
+
returnUrl = null;
|
|
534
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req, objForUpdate);
|
|
535
|
+
if (!objForUpdate) {
|
|
536
|
+
if (webhookUrl) {
|
|
537
|
+
try {
|
|
538
|
+
await axios_1.default.post(webhookUrl, {
|
|
539
|
+
status: 'FAILED',
|
|
540
|
+
message: 'Transaction Not Found',
|
|
541
|
+
ORDERID: req.body.ORDERID,
|
|
542
|
+
TXNID: req.body.TXNID
|
|
543
|
+
});
|
|
544
|
+
console.log("Sent webhook to ", webhookUrl, 'orderId:', req.body.ORDERID, 'txnId:', req.body.TXNID);
|
|
545
|
+
}
|
|
546
|
+
catch (e) {
|
|
547
|
+
console.log("Error sending webhook to ", webhookUrl, (e === null || e === void 0 ? void 0 : e.message) || e, 'orderId:', req.body.ORDERID, 'txnId:', req.body.TXNID);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (returnUrl) {
|
|
551
|
+
const separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
|
|
552
|
+
return res.redirect(`${returnUrl}${separator}status=FAILED&message=txn_not_found&ORDERID=${req.body.ORDERID}`);
|
|
553
|
+
}
|
|
554
|
+
res.send({ message: "Transaction Not Found !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID });
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (!["INITIATED", "TXN_PENDING", "PENDING"].includes(String(objForUpdate.status))) {
|
|
558
|
+
objForUpdate.readonly = "readonly";
|
|
559
|
+
if (webhookUrl) {
|
|
560
|
+
try {
|
|
561
|
+
await axios_1.default.post(webhookUrl, objForUpdate);
|
|
562
|
+
console.log("Sent webhook to ", webhookUrl, 'orderId:', req.body.ORDERID, 'txnId:', req.body.TXNID);
|
|
563
|
+
}
|
|
564
|
+
catch (e) {
|
|
565
|
+
console.log("Error sending webhook to ", webhookUrl, (e === null || e === void 0 ? void 0 : e.message) || e, 'orderId:', req.body.ORDERID, 'txnId:', req.body.TXNID);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (!fromWebhook) {
|
|
569
|
+
if (returnUrl) {
|
|
570
|
+
const separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
|
|
571
|
+
return res.redirect(`${returnUrl}${separator}status=${objForUpdate.status}&ORDERID=${objForUpdate.orderId}&TXNID=${objForUpdate.txnId}`);
|
|
572
|
+
}
|
|
573
|
+
(0, htmlhelper_1.renderView)(req, res, vp + "result.hbs", {
|
|
574
|
+
path_prefix: config.path_prefix,
|
|
575
|
+
...objForUpdate
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
res.send({ message: "Webhook already processed !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID, status: objForUpdate.status });
|
|
580
|
+
}
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (req.body.status === "paid" && !req.body.STATUS)
|
|
584
|
+
req.body.STATUS = "TXN_SUCCESS";
|
|
585
|
+
objForUpdate.status = req.body.STATUS;
|
|
586
|
+
objForUpdate.txnId = req.body.TXNID;
|
|
587
|
+
objForUpdate.extra = JSON.stringify(req.body);
|
|
588
|
+
try {
|
|
589
|
+
await this.db.update(this.tableNames.TRANSACTION, myquery, objForUpdate);
|
|
590
|
+
}
|
|
591
|
+
catch {
|
|
592
|
+
if (returnUrl) {
|
|
593
|
+
const separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
|
|
594
|
+
return res.redirect(`${returnUrl}${separator}status=FAILED&message=update_error&ORDERID=${req.body.ORDERID}`);
|
|
595
|
+
}
|
|
596
|
+
res.send({ message: "Error Occured !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID });
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (callbacks && typeof callbacks.onFinish === 'function') {
|
|
600
|
+
callbacks.onFinish(req.body.ORDERID, objForUpdate);
|
|
601
|
+
}
|
|
602
|
+
objForUpdate.readonly = "readonly";
|
|
603
|
+
if (webhookUrl) {
|
|
604
|
+
try {
|
|
605
|
+
await axios_1.default.post(webhookUrl, objForUpdate);
|
|
606
|
+
console.log("Sent webhook to ", webhookUrl, 'orderId:', req.body.ORDERID, 'txnId:', req.body.TXNID);
|
|
607
|
+
}
|
|
608
|
+
catch (e) {
|
|
609
|
+
console.log("Error sending webhook to ", webhookUrl, (e === null || e === void 0 ? void 0 : e.message) || e, 'orderId:', req.body.ORDERID, 'txnId:', req.body.TXNID);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
if (!fromWebhook) {
|
|
613
|
+
if (returnUrl) {
|
|
614
|
+
const separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
|
|
615
|
+
return res.redirect(`${returnUrl}${separator}status=${objForUpdate.status}&ORDERID=${objForUpdate.orderId}&TXNID=${objForUpdate.txnId}`);
|
|
616
|
+
}
|
|
617
|
+
(0, htmlhelper_1.renderView)(req, res, vp + "result.hbs", {
|
|
618
|
+
theme: config.theme,
|
|
619
|
+
path_prefix: config.path_prefix,
|
|
620
|
+
...objForUpdate
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
res.send({ message: "Transaction Updated !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID, status: objForUpdate.status });
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
async getOrder(req = { body: {}, query: {} }, orderId) {
|
|
628
|
+
const orderToFind = orderId || req.body.ORDERID || req.body.ORDER_ID || req.body.ORDER_ID || req.body.ORDERId || req.query.ORDERID || req.query.ORDERId || req.query.ORDER_ID || req.query.order_id;
|
|
629
|
+
const myquery = { orderId: orderToFind };
|
|
630
|
+
let objForUpdate = null;
|
|
631
|
+
try {
|
|
632
|
+
objForUpdate = await this.db.getOne(this.tableNames.TRANSACTION, myquery).catch(() => null);
|
|
633
|
+
}
|
|
634
|
+
catch {
|
|
635
|
+
objForUpdate = objForUpdate || null;
|
|
636
|
+
}
|
|
637
|
+
return objForUpdate;
|
|
638
|
+
}
|
|
639
|
+
async callback(req, res) {
|
|
640
|
+
console.log("request_data ", req.originalUrl, JSON.stringify(req.body));
|
|
641
|
+
// Normalize common order id and txn id field names (support ORDER_ID, ORDERID, etc.)
|
|
642
|
+
try {
|
|
643
|
+
if ((!req.body.ORDERID || req.body.ORDERID === '') && req.body.ORDER_ID) {
|
|
644
|
+
req.body.ORDERID = req.body.ORDER_ID;
|
|
645
|
+
}
|
|
646
|
+
if ((!req.body.TXNID || req.body.TXNID === '') && req.body.TXN_ID) {
|
|
647
|
+
req.body.TXNID = req.body.TXN_ID;
|
|
648
|
+
}
|
|
649
|
+
if ((!req.body.ORDERID || req.body.ORDERID === '') && req.query && req.query.order_id) {
|
|
650
|
+
req.body.ORDERID = req.query.order_id;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
catch {
|
|
654
|
+
// ignore
|
|
655
|
+
}
|
|
656
|
+
let result = false;
|
|
657
|
+
let isCancelled = false;
|
|
658
|
+
const objForUpdate = await this.getOrder(req);
|
|
659
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req, objForUpdate);
|
|
660
|
+
const payuInstance = this.getProviderInstance('PayU', (0, buildConfig_1.withClientConfigOverrides)(config, req, objForUpdate));
|
|
661
|
+
const openMoneyInstance = this.getProviderInstance('OpenMoney', (0, buildConfig_1.withClientConfigOverrides)(config, req, objForUpdate));
|
|
662
|
+
if (config.paytm_url) {
|
|
663
|
+
const checksumhash = req.body.CHECKSUMHASH;
|
|
664
|
+
if (checksumhash) {
|
|
665
|
+
result = await checksum_1.default.verifychecksum(req.body, config.KEY, checksumhash);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
const liveStatus = await this.getStatusFromPaytm({ MID: config.MID, ORDERID: req.body.ORDERID }, req.body.ORDERID, req);
|
|
669
|
+
// Merge important fields when live status is available
|
|
670
|
+
if (liveStatus && typeof liveStatus === 'object') {
|
|
671
|
+
req.body.STATUS = liveStatus.STATUS || req.body.STATUS;
|
|
672
|
+
req.body.TXNID = liveStatus.TXNID || req.body.TXNID;
|
|
673
|
+
}
|
|
674
|
+
result = liveStatus && liveStatus.STATUS == req.body.STATUS;
|
|
675
|
+
}
|
|
676
|
+
if (req.body.STATUS === 'TXN_FAILURE' && req.body.CANCELLED === "cancelled" && req.body.TXNID) {
|
|
677
|
+
isCancelled = true;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
else if (config.razor_url) {
|
|
681
|
+
let orderid = req.body.razorpay_order_id || req.query.ORDERID || req.query.order_id;
|
|
682
|
+
let liveResonse = null;
|
|
683
|
+
if (orderid) {
|
|
684
|
+
liveResonse = await this.getProviderInstance('Razorpay', config).orders.fetch(orderid).catch(() => null);
|
|
685
|
+
req.body.extras = liveResonse;
|
|
686
|
+
}
|
|
687
|
+
if (req.body.razorpay_payment_id) {
|
|
688
|
+
result = checksum_1.default.checkRazorSignature(req.body.razorpay_order_id, req.body.razorpay_payment_id, config.SECRET, req.body.razorpay_signature);
|
|
689
|
+
if (result) {
|
|
690
|
+
req.body.STATUS = 'TXN_SUCCESS';
|
|
691
|
+
req.body.ORDERID = req.body.razorpay_order_id;
|
|
692
|
+
req.body.TXNID = req.body.razorpay_payment_id;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
if (req.body.error && req.body.error.metadata && JSON.parse(req.body.error.metadata)) {
|
|
697
|
+
const orderId = JSON.parse(req.body.error.metadata).order_id;
|
|
698
|
+
req.body.razorpay_order_id = orderId;
|
|
699
|
+
}
|
|
700
|
+
req.body.STATUS = (liveResonse === null || liveResonse === void 0 ? void 0 : liveResonse.attempts) ? 'TXN_FAILURE' : 'CANCELLED';
|
|
701
|
+
req.body.ORDERID = req.body.razorpay_order_id || req.query.order_id;
|
|
702
|
+
isCancelled = true;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
else if (config.payu_url) {
|
|
706
|
+
const payuRest = await payuInstance.verifyResult(req);
|
|
707
|
+
result = !!payuRest.STATUS;
|
|
708
|
+
req.body.STATUS = payuRest.STATUS;
|
|
709
|
+
req.body.TXNID = payuRest.TXNID;
|
|
710
|
+
req.body.ORDERID = payuRest.ORDERID || req.query.order_id;
|
|
711
|
+
req.body.extras = payuRest.data;
|
|
712
|
+
isCancelled = !!payuRest.cancelled;
|
|
713
|
+
}
|
|
714
|
+
else if (config.open_money_url) {
|
|
715
|
+
const openRest = await openMoneyInstance.verifyResult(req);
|
|
716
|
+
result = true;
|
|
717
|
+
req.body.STATUS = openRest.STATUS;
|
|
718
|
+
req.body.TXNID = openRest.TXNID;
|
|
719
|
+
req.body.ORDERID = openRest.ORDERID || req.query.order_id;
|
|
720
|
+
req.body.extras = openRest.data;
|
|
721
|
+
}
|
|
722
|
+
console.log("NodePayTMPG::Transaction => ", req.body.ORDERID, req.body.STATUS);
|
|
723
|
+
if (result || isCancelled) {
|
|
724
|
+
await this.updateTransaction(req, res);
|
|
725
|
+
}
|
|
726
|
+
else {
|
|
727
|
+
res.send({ message: "Something went wrong ! Please try again later .", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID });
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
getServiceUsed(req, baseConfig) {
|
|
731
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(baseConfig, req);
|
|
732
|
+
let serviceUsed = '';
|
|
733
|
+
if (config.paytm_url)
|
|
734
|
+
serviceUsed = 'Paytm';
|
|
735
|
+
if (config.razor_url)
|
|
736
|
+
serviceUsed = 'Razorpay';
|
|
737
|
+
if (config.payu_url)
|
|
738
|
+
serviceUsed = 'PayU';
|
|
739
|
+
if (config.open_money_url)
|
|
740
|
+
serviceUsed = 'OpenMoney';
|
|
741
|
+
// https://razorpay.com/docs/webhooks/?preferred-country=IN
|
|
742
|
+
if (req.headers["x-razorpay-signature"])
|
|
743
|
+
serviceUsed = 'Razorpay';
|
|
744
|
+
// https://business.paytm.com/docs/payment-status/
|
|
745
|
+
else if (req.body.PAYMENTMODE)
|
|
746
|
+
serviceUsed = 'Paytm';
|
|
747
|
+
// https://docs.payu.in/docs/webhooks
|
|
748
|
+
else if (req.body.payment_source || req.body.merchantTransactionId)
|
|
749
|
+
serviceUsed = 'PayU';
|
|
750
|
+
return serviceUsed;
|
|
751
|
+
}
|
|
752
|
+
async webhook(req, res) {
|
|
753
|
+
try {
|
|
754
|
+
let config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
755
|
+
const payuInstance = this.getProviderInstance('PayU', config);
|
|
756
|
+
const openMoneyInstance = this.getProviderInstance('OpenMoney', config);
|
|
757
|
+
console.log("request_data ", req.originalUrl, JSON.stringify(req.body));
|
|
758
|
+
console.log("request_data rawBody", req.originalUrl, req.rawBody);
|
|
759
|
+
console.log("request_headers ", req.originalUrl, JSON.stringify(req.headers));
|
|
760
|
+
let serviceUsed = this.getServiceUsed(req, config);
|
|
761
|
+
if (serviceUsed === 'Paytm') {
|
|
762
|
+
await this.callback(req, res);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
if (serviceUsed === 'Razorpay') {
|
|
766
|
+
const events = ["payment.captured", "payment.pending", "payment.failed"];
|
|
767
|
+
if (req.body.event && events.indexOf(req.body.event) > -1) {
|
|
768
|
+
if (req.body.payload &&
|
|
769
|
+
req.body.payload.payment &&
|
|
770
|
+
req.body.payload.payment.entity) {
|
|
771
|
+
const entity = req.body.payload.payment.entity;
|
|
772
|
+
const razorpay_order_id = entity.order_id;
|
|
773
|
+
const order = await this.getOrder(undefined, razorpay_order_id);
|
|
774
|
+
config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req, order || undefined);
|
|
775
|
+
const razorpay_payment_id = entity.id;
|
|
776
|
+
const status = entity.status;
|
|
777
|
+
const event = req.body.event;
|
|
778
|
+
console.log(`Razorpay webhook payment order=${razorpay_order_id} payid=${razorpay_payment_id} status=${status}`);
|
|
779
|
+
const reqBody = req.rawBody;
|
|
780
|
+
const signature = req.headers["x-razorpay-signature"];
|
|
781
|
+
console.log("Razorpay webhook signature:", signature);
|
|
782
|
+
if (signature === undefined) {
|
|
783
|
+
res.status(200).send({ message: "Missing Razorpay signature" });
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
let signatureValid;
|
|
787
|
+
try {
|
|
788
|
+
signatureValid = razorpay_1.default.validateWebhookSignature(reqBody, signature, config.SECRET);
|
|
789
|
+
}
|
|
790
|
+
catch (e) {
|
|
791
|
+
signatureValid = false;
|
|
792
|
+
}
|
|
793
|
+
if (signatureValid) {
|
|
794
|
+
if (event === events[0]) {
|
|
795
|
+
req.body.STATUS = "TXN_SUCCESS";
|
|
796
|
+
}
|
|
797
|
+
else if (event === events[1]) { //pending
|
|
798
|
+
req.body.STATUS = "TXN_PENDING";
|
|
799
|
+
}
|
|
800
|
+
else { // failed
|
|
801
|
+
req.body.STATUS = "TXN_FAILURE";
|
|
802
|
+
}
|
|
803
|
+
req.body.ORDERID = razorpay_order_id;
|
|
804
|
+
req.body.TXNID = razorpay_payment_id;
|
|
805
|
+
setTimeout(() => {
|
|
806
|
+
this.updateTransaction(req, res, true);
|
|
807
|
+
}, 3000);
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
res.status(200).send({ message: "Invalid Rzpay signature" });
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
res.status(200).send({ message: "Invalid Payload" });
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
res.status(200).send({ message: "Unsupported event : " + req.body.event });
|
|
819
|
+
}
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
else if (serviceUsed === 'PayU') {
|
|
823
|
+
await payuInstance.processWebhook(req, res, this.updateTransaction.bind(this), this.getOrder.bind(this));
|
|
824
|
+
}
|
|
825
|
+
else if (serviceUsed === 'OpenMoney') {
|
|
826
|
+
await openMoneyInstance.processWebhook(req, res, this.updateTransaction.bind(this), this.getOrder.bind(this));
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
catch (e) {
|
|
830
|
+
console.error("Error in webhook processing", e);
|
|
831
|
+
try {
|
|
832
|
+
res.status(200).send({ message: "Received" });
|
|
833
|
+
}
|
|
834
|
+
catch (e) { }
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
async createTxn(req, res) {
|
|
838
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
839
|
+
// mandayory field
|
|
840
|
+
const requiredFields = ['NAME', 'EMAIL',
|
|
841
|
+
// 'MOBILE_NO',
|
|
842
|
+
'TXN_AMOUNT', 'PRODUCT_NAME'];
|
|
843
|
+
const checkedFields = [];
|
|
844
|
+
let gotAllParams = true;
|
|
845
|
+
requiredFields.forEach(field => {
|
|
846
|
+
if (!req.body[field]) {
|
|
847
|
+
gotAllParams = false;
|
|
848
|
+
checkedFields.push(field);
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
if (!gotAllParams) {
|
|
852
|
+
res.status(400).send({ message: "Missing required fields", missing: checkedFields });
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
try {
|
|
856
|
+
const user = await this.useController.create({ name: req.body.NAME, email: req.body.EMAIL, phone: req.body.MOBILE_NO });
|
|
857
|
+
let id = '';
|
|
858
|
+
if (config.paytm_url) {
|
|
859
|
+
id = "pay_" + makeid(config.id_length || IDLEN);
|
|
860
|
+
}
|
|
861
|
+
else if (config.razor_url) {
|
|
862
|
+
const options = {
|
|
863
|
+
amount: req.body.TXN_AMOUNT * 100,
|
|
864
|
+
currency: "INR",
|
|
865
|
+
receipt: user.id + '_' + Date.now()
|
|
866
|
+
};
|
|
867
|
+
const razorPayInstance = this.getProviderInstance('Razorpay', config);
|
|
868
|
+
const order = await razorPayInstance.orders.create(options);
|
|
869
|
+
id = order.id;
|
|
870
|
+
}
|
|
871
|
+
else if (config.payu_url) {
|
|
872
|
+
id = "payu_" + makeid(config.id_length || IDLEN);
|
|
873
|
+
}
|
|
874
|
+
else if (config.open_money_url) {
|
|
875
|
+
id = "pay_" + makeid(config.id_length || IDLEN);
|
|
876
|
+
}
|
|
877
|
+
const txnTask = {
|
|
878
|
+
id: id,
|
|
879
|
+
orderId: id,
|
|
880
|
+
cusId: user.id,
|
|
881
|
+
time: Date.now(),
|
|
882
|
+
status: 'INITIATED',
|
|
883
|
+
name: user.name,
|
|
884
|
+
email: user.email,
|
|
885
|
+
phone: user.phone,
|
|
886
|
+
amount: req.body.TXN_AMOUNT,
|
|
887
|
+
pname: req.body.PRODUCT_NAME,
|
|
888
|
+
returnUrl: req.body.RETURN_URL || '',
|
|
889
|
+
webhookUrl: req.body.WEBHOOK_URL || '',
|
|
890
|
+
extra: (req.body.EXTRA || ''),
|
|
891
|
+
state: req.body.STATE || '',
|
|
892
|
+
clientId: req.body.CLIENT_ID || ''
|
|
893
|
+
};
|
|
894
|
+
const txn = await this.insertTransactionInDb(txnTask);
|
|
895
|
+
const urlData64 = this.encodeTxnDataForUrl(JSON.stringify({
|
|
896
|
+
NAME: txn.name,
|
|
897
|
+
EMAIL: txn.email,
|
|
898
|
+
MOBILE_NO: txn.phone,
|
|
899
|
+
ORDER_ID: txn.orderId,
|
|
900
|
+
RETURN_URL: txn.returnUrl,
|
|
901
|
+
WEBHOOK_URL: txn.webhookUrl,
|
|
902
|
+
TXN_AMOUNT: txn.amount,
|
|
903
|
+
PRODUCT_NAME: txn.pname,
|
|
904
|
+
clientId: txn.clientId,
|
|
905
|
+
CLIENT_ID: txn.clientId,
|
|
906
|
+
STATE: txn.state,
|
|
907
|
+
}), req);
|
|
908
|
+
txn.payurl = config.host_url + '/' + config.path_prefix + '/init?to=' + urlData64;
|
|
909
|
+
if (txn.clientId) {
|
|
910
|
+
txn.payurl += `&client_id=${txn.clientId}`;
|
|
911
|
+
}
|
|
912
|
+
res.send(txn);
|
|
913
|
+
}
|
|
914
|
+
catch (err) {
|
|
915
|
+
console.log(err);
|
|
916
|
+
res.redirect('');
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
;
|
|
920
|
+
async createTxnToken(req, res) {
|
|
921
|
+
return this.createTxn(req, res);
|
|
922
|
+
}
|
|
923
|
+
;
|
|
924
|
+
// optional user
|
|
925
|
+
async getTransactions(req, res) {
|
|
926
|
+
// parameters can be from query or body
|
|
927
|
+
// MID, MOBILE_NO, PRODUCT_NAME, EMAIL, NAME, limit, offset
|
|
928
|
+
const params = { ...(req.query || {}), ...(req.body || {}) };
|
|
929
|
+
// Build query map from incoming fields to db columns
|
|
930
|
+
const query = {};
|
|
931
|
+
const fieldMap = {
|
|
932
|
+
MOBILE_NO: 'phone',
|
|
933
|
+
PRODUCT_NAME: 'pname',
|
|
934
|
+
EMAIL: 'email',
|
|
935
|
+
NAME: 'name',
|
|
936
|
+
ORDER_ID: 'orderId',
|
|
937
|
+
ORDERID: 'orderId',
|
|
938
|
+
STATUS: 'status',
|
|
939
|
+
email: 'email',
|
|
940
|
+
phone: 'phone',
|
|
941
|
+
name: 'name',
|
|
942
|
+
product_name: 'pname',
|
|
943
|
+
order_id: 'orderId',
|
|
944
|
+
status: 'status',
|
|
945
|
+
mobile_no: 'phone',
|
|
946
|
+
CLIENT_ID: 'clientId',
|
|
947
|
+
state: 'state',
|
|
948
|
+
STATE: 'state',
|
|
949
|
+
clientId: 'clientId',
|
|
950
|
+
WEBHOOK_URL: 'webhookUrl',
|
|
951
|
+
webhookUrl: 'webhookUrl',
|
|
952
|
+
};
|
|
953
|
+
Object.keys(fieldMap).forEach((key) => {
|
|
954
|
+
if (params[key]) {
|
|
955
|
+
query[fieldMap[key]] = params[key];
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
// Pagination
|
|
959
|
+
const limit = Math.min(parseInt(params.limit, 10) || 20, 100);
|
|
960
|
+
const offset = Math.max(parseInt(params.offset, 10) || 0, 0);
|
|
961
|
+
try {
|
|
962
|
+
const all = await this.db.get(this.tableNames.TRANSACTION, query, {
|
|
963
|
+
sort: [{ field: 'time', order: 'desc' }],
|
|
964
|
+
limit: limit,
|
|
965
|
+
offset: offset
|
|
966
|
+
});
|
|
967
|
+
res.send({
|
|
968
|
+
limit,
|
|
969
|
+
offset,
|
|
970
|
+
count: all.length,
|
|
971
|
+
transactions: all
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
catch (err) {
|
|
975
|
+
console.log('getTransactions error', err);
|
|
976
|
+
res.status(500).send({ message: 'Failed to fetch transactions', error: (err === null || err === void 0 ? void 0 : err.message) || 'unknown_error' });
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
async status(req, res) {
|
|
980
|
+
if (!req.body.ORDERID && req.query.ORDERID) {
|
|
981
|
+
req.body.ORDERID = req.query.ORDERID;
|
|
982
|
+
}
|
|
983
|
+
if (!req.body.ORDER_ID && req.query.ORDER_ID) {
|
|
984
|
+
req.body.ORDER_ID = req.query.ORDER_ID;
|
|
985
|
+
}
|
|
986
|
+
if (!req.body.ORDER_ID && req.body.ORDERID) {
|
|
987
|
+
req.body.ORDER_ID = req.body.ORDERID;
|
|
988
|
+
}
|
|
989
|
+
if (!req.body.ORDER_ID) {
|
|
990
|
+
res.status(400).send({ message: "Missing ORDER_ID" });
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
const myquery = { orderId: req.body.ORDER_ID };
|
|
994
|
+
const orderData = await this.db.getOne(this.tableNames.TRANSACTION, myquery).catch((err) => {
|
|
995
|
+
res.send(err);
|
|
996
|
+
return null;
|
|
997
|
+
});
|
|
998
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req, orderData);
|
|
999
|
+
const callbacks = this.callbacks;
|
|
1000
|
+
const payuInstance = this.getProviderInstance('PayU', config);
|
|
1001
|
+
const openMoneyInstance = this.getProviderInstance('OpenMoney', config);
|
|
1002
|
+
const razorPayInstance = this.getProviderInstance('Razorpay', config);
|
|
1003
|
+
if (!orderData) {
|
|
1004
|
+
if (!res.headersSent) {
|
|
1005
|
+
res.send({ message: "Order Not Found or not initiated yet!", ORDER_ID: req.body.ORDER_ID });
|
|
1006
|
+
}
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
if (orderData.status === "INITIATED") {
|
|
1010
|
+
const params = {};
|
|
1011
|
+
params["MID"] = config.MID;
|
|
1012
|
+
params["ORDERID"] = req.body.ORDER_ID;
|
|
1013
|
+
const onStatusUpdate = async (paytmResponse) => {
|
|
1014
|
+
if (paytmResponse.TXNID && paytmResponse.TXNID.length > 4) {
|
|
1015
|
+
orderData.status = paytmResponse.STATUS;
|
|
1016
|
+
orderData.extra = JSON.stringify(paytmResponse);
|
|
1017
|
+
try {
|
|
1018
|
+
await this.db.update(this.tableNames.TRANSACTION, myquery, orderData);
|
|
1019
|
+
}
|
|
1020
|
+
catch (err) {
|
|
1021
|
+
res.send({ message: "Error Occured !", ORDERID: paytmResponse.ORDERID, TXNID: paytmResponse.TXNID });
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
if (callbacks && typeof callbacks.onFinish === 'function') {
|
|
1025
|
+
callbacks.onFinish(req.body.ORDER_ID, orderData);
|
|
1026
|
+
}
|
|
1027
|
+
res.send(paytmResponse);
|
|
1028
|
+
}
|
|
1029
|
+
else {
|
|
1030
|
+
res.send(orderData);
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
if (config.paytm_url) {
|
|
1034
|
+
const paytmResponse = await this.getStatusFromPaytm(params, req.body.ORDER_ID, req);
|
|
1035
|
+
await onStatusUpdate(paytmResponse);
|
|
1036
|
+
}
|
|
1037
|
+
else if (config.razor_url) {
|
|
1038
|
+
let result = await razorPayInstance.orders.fetch(req.body.ORDER_ID);
|
|
1039
|
+
result.ORDERID = req.body.ORDER_ID;
|
|
1040
|
+
if (result.status == 'paid' && result.amount_due == 0) {
|
|
1041
|
+
result.STATUS = 'TXN_SUCCESS';
|
|
1042
|
+
let payments = await razorPayInstance.orders.fetchPayments(req.body.ORDER_ID);
|
|
1043
|
+
payments.items.forEach((item) => {
|
|
1044
|
+
if (item.status == 'captured') {
|
|
1045
|
+
result.TXNID = item.id;
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
result.payments = payments;
|
|
1049
|
+
await onStatusUpdate(result);
|
|
1050
|
+
}
|
|
1051
|
+
else {
|
|
1052
|
+
res.send(orderData);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
else if (config.payu_url) {
|
|
1056
|
+
let result = await payuInstance.getPaymentStatus(req.body.ORDER_ID);
|
|
1057
|
+
if (result && result.transaction_details && result.transaction_details[req.body.ORDER_ID]) {
|
|
1058
|
+
let txn = result.transaction_details[req.body.ORDER_ID];
|
|
1059
|
+
let status = 'TXN_FAILURE';
|
|
1060
|
+
if (txn.status == 'success') {
|
|
1061
|
+
status = 'TXN_SUCCESS';
|
|
1062
|
+
}
|
|
1063
|
+
else if (txn.status == 'pending') {
|
|
1064
|
+
status = 'TXN_PENDING';
|
|
1065
|
+
}
|
|
1066
|
+
await onStatusUpdate({
|
|
1067
|
+
STATUS: status,
|
|
1068
|
+
ORDERID: req.body.ORDER_ID,
|
|
1069
|
+
TXNID: txn.mihpayid || txn.txnid,
|
|
1070
|
+
payu: txn
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
else {
|
|
1074
|
+
res.send(orderData);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
else if (config.open_money_url) {
|
|
1078
|
+
let extras = JSON.parse(orderData.extra);
|
|
1079
|
+
if (!extras || !extras.layer_pay_token_id) {
|
|
1080
|
+
res.status(500);
|
|
1081
|
+
res.send({ message: 'An unexpected error occured. No payment token exists' });
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
let result = await openMoneyInstance.getPaymentStatus(extras.layer_pay_token_id);
|
|
1085
|
+
result = JSON.parse(result);
|
|
1086
|
+
result.ORDERID = req.body.ORDER_ID;
|
|
1087
|
+
if (result.status == 'paid' || result.status == 'captured') {
|
|
1088
|
+
result.STATUS = 'TXN_SUCCESS';
|
|
1089
|
+
result.TXNID = result.id;
|
|
1090
|
+
await onStatusUpdate(result);
|
|
1091
|
+
}
|
|
1092
|
+
else if (result.status == 'pending' || result.status == 'attempted') {
|
|
1093
|
+
result.STATUS = 'TXN_PENDING';
|
|
1094
|
+
result.TXNID = result.id;
|
|
1095
|
+
await onStatusUpdate(result);
|
|
1096
|
+
}
|
|
1097
|
+
// else if (result.status == 'failed' || result.status == 'cancelled') {
|
|
1098
|
+
// result.STATUS = 'TXN_FAILED'
|
|
1099
|
+
// result.TXNID = result.id
|
|
1100
|
+
// onStatusUpdate(result)
|
|
1101
|
+
// }
|
|
1102
|
+
else {
|
|
1103
|
+
res.send(orderData);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
else {
|
|
1108
|
+
res.send(orderData);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
async getStatusFromPaytm(params, orderId, req) {
|
|
1112
|
+
const checksum = await this.generateChecksum(params, req);
|
|
1113
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
1114
|
+
try {
|
|
1115
|
+
const resp = await axios_1.default.post(`${config.paytm_url}/order/status`, { MID: config.MID, ORDERID: orderId, CHECKSUMHASH: checksum });
|
|
1116
|
+
if (resp.status === 200) {
|
|
1117
|
+
return resp.data;
|
|
1118
|
+
}
|
|
1119
|
+
console.log('ERROR:::', resp.status, '\n', resp.data);
|
|
1120
|
+
return { message: 'Error Occured !', ORDERID: orderId };
|
|
1121
|
+
}
|
|
1122
|
+
catch (err) {
|
|
1123
|
+
console.log('ERROR:::', err);
|
|
1124
|
+
return { message: 'Error Occured !', ORDERID: orderId };
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
exports.PaymentController = PaymentController;
|