Commit 385d8ff5945e244b3d4efaee1f68c8b54753cc7b

Authored by Adhidarma Hadiwinoto
1 parent d4661aa843
Exists in master

PREPAID topup tested

Showing 7 changed files with 158 additions and 7 deletions Inline Diff

lib/callback/dumper.js
1 const MODULE_NAME = 'CALLBACK.DUMPER'; 1 const MODULE_NAME = 'CALLBACK.DUMPER';
2 2
3 const fs = require('fs'); 3 const fs = require('fs');
4 const fsPromise = require('fs').promises; 4 const fsPromise = require('fs').promises;
5 const path = require('path'); 5 const path = require('path');
6 const config = require('komodo-sdk/config'); 6 const config = require('komodo-sdk/config');
7 const logger = require('komodo-sdk/logger'); 7 const logger = require('komodo-sdk/logger');
8 const moment = require('moment'); 8 const moment = require('moment');
9 9
10 const baseDumpDir = 'dump'; 10 const baseDumpDir = 'dump';
11 const callbackDumpDir = path.join(baseDumpDir, 'callback'); 11 const callbackDumpDir = path.join(baseDumpDir, 'callback');
12 12
13 if (!fs.existsSync(baseDumpDir)) { 13 if (!fs.existsSync(baseDumpDir)) {
14 logger.verbose(`${MODULE_NAME} D7E3D88E: Creating base dump dir`); 14 logger.verbose(`${MODULE_NAME} D7E3D88E: Creating base dump dir`);
15 fs.mkdirSync(baseDumpDir); 15 fs.mkdirSync(baseDumpDir);
16 } 16 }
17 17
18 if (!fs.existsSync(callbackDumpDir)) { 18 if (!fs.existsSync(callbackDumpDir)) {
19 logger.verbose(`${MODULE_NAME} 002EC4A8: Creating callback dump dir`); 19 logger.verbose(`${MODULE_NAME} 002EC4A8: Creating callback dump dir`);
20 fs.mkdirSync(callbackDumpDir); 20 fs.mkdirSync(callbackDumpDir);
21 } 21 }
22 22
23 module.exports = async (req, res, next) => { 23 module.exports = async (req, res, next) => {
24 if ( 24 if (
25 !config 25 !config
26 || !config.partner 26 || !config.partner
27 || !config.partner.callback 27 || !config.partner.callback
28 || !config.partner.callback.dump_request 28 || !config.partner.callback.dump_request
29 ) { 29 ) {
30 next(); 30 next();
31 return; 31 return;
32 } 32 }
33 33
34 const { xid } = res.locals; 34 const { xid } = res.locals;
35 35
36 const data = `-------- 36 const data = `--------
37 XID: ${xid} 37 XID: ${xid}
38 PID: ${process.pid}
38 DATE: ${moment().format('YYYY-MM-DD HH:mm:ss.SSS')} 39 DATE: ${moment().format('YYYY-MM-DD HH:mm:ss.SSS')}
39 REMOTE-ADDR: ${req.ip} 40 REMOTE-ADDR: ${req.ip}
40 USER-AGENT: ${req.get('user-agent')} 41 USER-AGENT: ${req.get('user-agent')}
41 METHOD: ${req.method} 42 METHOD: ${req.method}
42 URL: ${req.url} 43 URL: ${req.url}
43 44
44 QUERY-STRING: 45 QUERY-STRING:
45 ${JSON.stringify(req.query, null, 2)} 46 ${JSON.stringify(req.query, null, 2)}
46 47
47 REQ-CONTENT-TYPE: ${req.get('content-type')} 48 REQ-CONTENT-TYPE: ${req.get('content-type')}
48 REQ-BODY-TYPEOF: ${typeof req.body} 49 REQ-BODY-TYPEOF: ${typeof req.body}
49 REQ-BODY: 50 REQ-BODY:
50 ${(req.body && typeof req.body === 'object' && JSON.stringify(req.body, null, 2)) || req.body} 51 ${(req.body && typeof req.body === 'object' && JSON.stringify(req.body, null, 2)) || req.body}
51 `; 52 `;
52 53
53 const dumpDir = path.join( 54 const dumpDir = path.join(
54 callbackDumpDir, 55 callbackDumpDir,
55 moment().format('YYYY-MM-DD'), 56 moment().format('YYYY-MM-DD'),
56 ); 57 );
57 58
58 try { 59 try {
59 await fsPromise.stat(dumpDir); 60 await fsPromise.stat(dumpDir);
60 } catch { 61 } catch {
61 await fsPromise.mkdir(dumpDir, { recursive: true }); 62 await fsPromise.mkdir(dumpDir, { recursive: true });
62 } 63 }
63 64
64 await fsPromise.writeFile( 65 await fsPromise.writeFile(
65 path.join( 66 path.join(
66 dumpDir, 67 dumpDir,
67 [ 68 [
68 moment().format('YYMMDD_HHmmss_SSS'), 69 moment().format('YYMMDD_HHmmss_SSS'),
69 xid, 70 xid,
70 ].join('_'), 71 ].join('_'),
71 ), 72 ),
72 data, 73 data,
73 ); 74 );
74 75
75 await fsPromise.writeFile( 76 await fsPromise.writeFile(
76 path.join(callbackDumpDir, 'last.trx'), 77 path.join(callbackDumpDir, 'last-callback'),
77 data, 78 data,
78 ); 79 );
79 80
80 next(); 81 next();
81 }; 82 };
82 83
lib/callback/handler-prepaid.js
1 const MODULE_NAME = 'CALLBACK.HANDLER-PREPAID';
2
3 const logger = require('komodo-sdk/logger');
4 const report = require('../report/prepaid');
5 const getFromReq = require('../get-from-params-body-qs');
6 const translateRc = require('../translate-rc');
7
1 module.exports = (req, res) => { 8 module.exports = (req, res) => {
2 const { xid } = res.locals; 9 const { xid } = res.locals;
3 res.json({ 10 res.json({
4 status: 'OK', 11 status: 'OK',
5 error: null, 12 error: null,
6 ts: new Date(), 13 ts: new Date(),
7 xid, 14 xid,
8 }); 15 });
16
17 const rcFromRequest = getFromReq(req, 'rc');
18 const translatedRc = rcFromRequest && (translateRc[rcFromRequest] || rcFromRequest);
19 logger.verbose(`${MODULE_NAME} 2B7BE44D: RC translated`, {
20 xid,
21 rcFromRequest,
22 translatedRc,
23 });
24
25 report(xid, {
26 trx_id: getFromReq(req, 'request_id'),
27 rc: translatedRc || '68',
28 sn: getFromReq(req, 'sn'),
29 amount: getFromReq(req, 'amount'),
30 balance: getFromReq(req, 'ending_balance'),
31 message: {
32 xid,
33 CALLBACK: {
34 ip: req.ip,
35 method: req.method,
36 body: req.body,
37 qs: req.query,
38 },
39 },
40 });
9 }; 41 };
10 42
lib/get-from-params-body-qs.js
File was created 1 module.exports = (req, keyword) => (req.params && req.params[keyword])
2 || (req.body && req.body[keyword])
3 || (req.query && req.query[keyword]);
4
lib/hit/dump-req-res.js
File was created 1 const MODULE_NAME = 'DUMP-REQ-RES';
2
3 const fs = require('fs');
4 const path = require('path');
5 const fsPromise = require('fs').promises;
6 const moment = require('moment');
7
8 const config = require('komodo-sdk/config');
9 const logger = require('komodo-sdk/logger');
10
11 const baseDumpDir = 'dump';
12 const requestDumpDir = path.join(baseDumpDir, 'request');
13
14 if (!fs.existsSync(baseDumpDir)) {
15 logger.verbose(`${MODULE_NAME} 51105314: Creating base dump dir`);
16 fs.mkdirSync(baseDumpDir);
17 }
18
19 if (!fs.existsSync(requestDumpDir)) {
20 logger.verbose(`${MODULE_NAME} 8A52891B: Creating request dump dir`);
21 fs.mkdirSync(requestDumpDir);
22 }
23
24 module.exports = async (
25 xid, task, httpMethod, endpointUrl, params, responseBody, responseStatus,
26 ) => {
27 if (
28 !config
29 || !config.partner
30 || !config.partner.dump_request
31 ) {
32 return;
33 }
34
35 const data = `--------
36 XID: ${xid}
37 PID: ${process.pid}
38 DATE: ${moment().format('YYYY-MM-DD HH:mm:ss.SSS')}
39
40 TASK:
41 ${JSON.stringify(task, null, 2)}
42
43
44 HTTP-METHOD: ${httpMethod}
45 URL: ${endpointUrl}
46 PARAMS:
47 ${
48 params && (
49 (typeof params === 'string' && params)
50 || JSON.stringify(params, null, 2)
51 )
52 }
53
54 RESPONSE-STATUS: ${responseStatus}
55 RESPONSE-BODY:
56 ${
57 responseBody && (
58 (typeof responseBody === 'string' && responseBody)
59 || JSON.stringify(responseBody, null, 2)
60 )
61 }
62 `;
63
64 const dumpDir = path.join(
65 requestDumpDir,
66 moment().format('YYYY-MM-DD'),
67 );
68
69 try {
70 await fsPromise.stat(dumpDir);
71 } catch {
72 await fsPromise.mkdir(dumpDir, { recursive: true });
73 }
74
75 await fsPromise.writeFile(
76 path.join(
77 dumpDir,
78 `trx_${task.trx_id}`,
79 ),
80 data,
81 );
82
83 await fsPromise.writeFile(
84 path.join(dumpDir, 'last-trx'),
85 data,
86 );
87 };
88
1 const MODULE_NAME = 'HIT.PREPAID'; 1 const MODULE_NAME = 'HIT.PREPAID';
2 2
3 const axios = require('axios').default; 3 const axios = require('axios').default;
4 const urljoin = require('url-join'); 4 const urljoin = require('url-join');
5 const uniqid = require('uniqid'); 5 const uniqid = require('uniqid');
6 6
7 const config = require('komodo-sdk/config'); 7 const config = require('komodo-sdk/config');
8 const logger = require('komodo-sdk/logger'); 8 const logger = require('komodo-sdk/logger');
9 9
10 const report = require('../report/prepaid'); 10 const report = require('../report/prepaid');
11 const translateRc = require('../translate-rc'); 11 const translateRc = require('../translate-rc');
12 const composeCallbackUrl = require('./compose-callback-url'); 12 const composeCallbackUrl = require('./compose-callback-url');
13 const dumpReqRes = require('./dump-req-res');
13 14
14 module.exports = async (task, isAdvice) => { 15 module.exports = async (task, isAdvice) => {
15 const xid = uniqid(); 16 const xid = uniqid();
16 17
18 logger.verbose(`${MODULE_NAME} 2272F01F: Processing task`, {
19 xid,
20 isAdvice,
21 task,
22 });
23
17 const params = { 24 const params = {
18 request_id: task.trx_id, 25 request_id: task.trx_id,
19 terminal_name: config.partner.terminal_name, 26 terminal_name: config.partner.terminal_name,
20 password: config.partner.password, 27 password: config.partner.password,
21 destination: task.destination, 28 destination: task.destination,
22 product_name: task.remote_product, 29 product_name: task.remote_product,
23 reverse_url: composeCallbackUrl(xid, false), 30 reverse_url: composeCallbackUrl(xid, false),
24 }; 31 };
25 32
26 const endpointUrl = isAdvice 33 const endpointUrl = isAdvice
27 ? urljoin(config.partner.url, '/trx-status') 34 ? urljoin(config.partner.url, '/trx-status')
28 : urljoin(config.partner.url, '/topup'); 35 : urljoin(config.partner.url, '/topup');
29 36
37 let lastResponse = null;
38
30 try { 39 try {
31 logger.verbose(`${MODULE_NAME} 4AAD4F99: Going to HIT prepaid endpoint`, { 40 logger.verbose(`${MODULE_NAME} 4AAD4F99: Going to HIT prepaid endpoint`, {
32 xid, 41 xid,
33 isAdvice,
34 endpointUrl, 42 endpointUrl,
35 params, 43 params,
36 }); 44 });
37 45
38 const response = await axios.get(endpointUrl, { 46 const response = await axios.get(endpointUrl, {
39 headers: { 47 headers: {
40 'User-Agent': 'KOMODO-GW-HTTPGETX', 48 'User-Agent': 'KOMODO-GW-HTTPGETX',
41 }, 49 },
42 timeout: config.partner.hit_timeout_ms || 60 * 1000, 50 timeout: config.partner.hit_timeout_ms || 60 * 1000,
43 params, 51 params,
44 }); 52 });
45 53
46 if (!response) { 54 if (!response) {
47 const e = new Error(`${MODULE_NAME} 8CF4E04D: Empty response`); 55 const e = new Error(`${MODULE_NAME} 8CF4E04D: Empty response`);
48 e.rc = '68'; 56 e.rc = '68';
49 e.response = response; 57 e.response = response;
50 throw e; 58 throw e;
51 } 59 }
52 60
53 if (!response.data) { 61 if (!response.data) {
54 const e = new Error(`${MODULE_NAME} E72B5A53: Empty response data`); 62 const e = new Error(`${MODULE_NAME} E72B5A53: Empty response data`);
55 e.rc = '68'; 63 e.rc = '68';
56 e.response = response; 64 e.response = response;
57 throw e; 65 throw e;
58 } 66 }
59 67
60 if (typeof response.data !== 'object') { 68 if (typeof response.data !== 'object') {
61 const e = new Error(`${MODULE_NAME} 507680AB: Response data is not a JSON`); 69 const e = new Error(`${MODULE_NAME} 507680AB: Response data is not a JSON`);
62 e.rc = '68'; 70 e.rc = '68';
63 e.response = response; 71 e.response = response;
64 throw e; 72 throw e;
65 } 73 }
66 74
75 lastResponse = response;
76
67 logger.verbose(`${MODULE_NAME} E51AFBBA: Got a direct response`, { 77 logger.verbose(`${MODULE_NAME} E51AFBBA: Got a direct response`, {
68 xid, 78 xid,
69 responseBody: response.data, 79 responseBody: response.data,
70 }); 80 });
71 81
72 report(xid, { 82 report(xid, {
73 trx_id: task.trx_id, 83 trx_id: task.trx_id,
74 rc: response.data.rc ? translateRc[response.data.rc] || response.data.rc 84 rc: response.data.rc ? translateRc[response.data.rc] || response.data.rc
75 : '68', 85 : '68',
76 sn: response.data.sn || null, 86 sn: response.data.sn || null,
77 amount: 0, 87 amount: 0,
78 balance: 0, 88 balance: 0,
79 message: { 89 message: {
80 xid, 90 xid,
81 'DIRECT-RESPONSE': response.data, 91 'DIRECT-RESPONSE': response.data,
82 'IS-ADVICE': !!isAdvice, 92 'IS-ADVICE': !!isAdvice,
83 }, 93 },
84 }); 94 });
85 } catch (e) { 95 } catch (e) {
86 const rc = e.rc || '68'; 96 const rc = e.rc || '68';
87 97
98 lastResponse = e.response;
99
88 report(xid, { 100 report(xid, {
89 trx_id: task.trx_id, 101 trx_id: task.trx_id,
90 rc, 102 rc,
91 message: { 103 message: {
92 xid, 104 xid,
93 'KOMODO-GW-ERROR': { 105 'KOMODO-GW-ERROR': {
94 eCode: e.code, 106 eCode: e.code,
95 eMessage: e.message, 107 eMessage: e.message,
96 responseHttpStatus: e.response && e.response.status, 108 responseHttpStatus: e.response && e.response.status,
97 responseBody: e.response && e.response.data, 109 responseBody: e.response && e.response.data,
98 }, 110 },
99 'IS-ADVICE': !!isAdvice, 111 'IS-ADVICE': !!isAdvice,
100 }, 112 },
101 }); 113 });
114 } finally {
115 dumpReqRes(
116 xid,
117 task,
118 'GET',
119 endpointUrl,
120 params,
121 lastResponse && lastResponse.data,
122 lastResponse && lastResponse.status,
123 lastResponse,
124 );
102 } 125 }
103 }; 126 };
lib/partner-prepaid.js
1 // const MODULE_NAME = 'PARTNER-PREPAID'; 1 const hit = require('./hit/prepaid');
2 2
3 exports.buy = () => { 3 exports.buy = (task) => {
4 // 4 hit(task, false);
5 }; 5 };
6 6
7 exports.advice = () => { 7 exports.advice = (task) => {
8 // 8 hit(task, true);
9 }; 9 };
10 10
1 module.exports = { 1 module.exports = {
2 '00': '00', 2 '00': '00',
3 '02': '90',
4 '03': '90',
5 '04': '90',
3 13: '90', 6 13: '90',
4 30: '40', 7 30: '40',
5 68: '68', 8 68: '68',
6 91: '90', 9 91: '90',
10 92: '90',
7 93: '94', 11 93: '94',
8 96: '68', 12 96: '68',
13 97: '90',
9 }; 14 };
10 15