Compare View

switch
from
...
to
 
Commits (2)

Changes

Showing 2 changed files Inline Diff

lib/core-callback/sender.js
1 const MODULE_NAME = 'CORE-CALLBACK.SENDER'; 1 const MODULE_NAME = 'CORE-CALLBACK.SENDER';
2 2
3 const axios = require('axios'); 3 const axios = require('axios');
4 const config = require('komodo-sdk/config'); 4 const config = require('komodo-sdk/config');
5 const logger = require('tektrans-logger'); 5 const logger = require('tektrans-logger');
6 6
7 const dumper = require('./dumper/sender'); 7 const dumper = require('./dumper/sender');
8 const matrix = require('../matrix'); 8 const matrix = require('../matrix');
9 9
10 const webhookSender = require('../webhook-sender'); 10 const webhookSender = require('../webhook-sender');
11 11
12 const HTTP_TIMEOUT = Number( 12 const HTTP_TIMEOUT = Number(
13 config.callback_sender && config.callback_sender.http_timeout_ms, 13 config.callback_sender && config.callback_sender.http_timeout_ms,
14 ) || 30 * 1000; 14 ) || 30 * 1000;
15 15
16 const SLEEP_BEFORE_RETRY_MS = Number( 16 const SLEEP_BEFORE_RETRY_MS = Number(
17 config.callback_sender && config.callback_sender.sleep_before_retry_ms, 17 config.callback_sender && config.callback_sender.sleep_before_retry_ms,
18 ) || 10 * 1000; 18 ) || 10 * 1000;
19 19
20 const MAX_RETRY = Number( 20 const MAX_RETRY = Number(
21 config.callback_sender && config.callback_sender.max_retry, 21 config.callback_sender && config.callback_sender.max_retry,
22 ) || 10; 22 ) || 10;
23 23
24 logger.verbose(`${MODULE_NAME} 848B9104: Initialized`, { 24 logger.verbose(`${MODULE_NAME} 848B9104: Initialized`, {
25 HTTP_TIMEOUT, 25 HTTP_TIMEOUT,
26 SLEEP_BEFORE_RETRY_MS, 26 SLEEP_BEFORE_RETRY_MS,
27 MAX_RETRY, 27 MAX_RETRY,
28 }); 28 });
29 29
30 const axiosHeaders = { 30 const axiosHeaders = {
31 'Content-Type': 'application/json', 31 'Content-Type': 'application/json',
32 'User-Agent': 'KOMODO-HTTPGETX callback sender', 32 'User-Agent': 'KOMODO-HTTPGETX callback sender',
33 }; 33 };
34 34
35 const sleep = require('../sleep'); 35 const sleep = require('../sleep');
36 const urlConcatQs = require('../url-concat-qs'); 36 const urlConcatQs = require('../url-concat-qs');
37 37
38 const sender = async (data, xid, retry) => { 38 const sender = async (data, xid, retry) => {
39 const params = { 39 const params = {
40 httpgetx_xid: xid, 40 httpgetx_xid: xid,
41 command: data.command, 41 command: data.command,
42 42
43 request_id: data.request_id && data.request_id.toString(), 43 request_id: data.request_id && data.request_id.toString(),
44 transaction_id: data.transaction_id && data.transaction_id.toString(), 44 transaction_id: data.transaction_id && data.transaction_id.toString(),
45 transaction_date: data.transaction_date, 45 transaction_date: data.transaction_date,
46 46
47 store_name: data.store_name, 47 store_name: data.store_name,
48 terminal_name: data.terminal_name, 48 terminal_name: data.terminal_name,
49 49
50 product_name: data.product_name, 50 product_name: data.product_name,
51 destination: data.destination, 51 destination: data.destination,
52 52
53 rc: data.rc, 53 rc: data.rc,
54 sn: data.sn || undefined, 54 sn: data.sn || undefined,
55 amount: Number(data.amount) || undefined, 55 amount: Number(data.amount) || undefined,
56 ending_balance: Number(data.ending_balance) || undefined, 56 ending_balance: Number(data.ending_balance) || undefined,
57 57
58 message: data.message, 58 message: data.message,
59 59
60 bill_count: Number(data.bill_count) || undefined, 60 bill_count: Number(data.bill_count) || undefined,
61 bill_amount: Number(data.bill_amount) || undefined, 61 bill_amount: Number(data.bill_amount) || undefined,
62 fee_per_bill: Number(data.fee) || undefined, 62 fee_per_bill: Number(data.fee) || undefined,
63 fee_total: Number(data.fee_total) || undefined, 63 fee_total: Number(data.fee_total) || undefined,
64 64
65 bill_detail: data.bill_detail || undefined, 65 bill_detail: data.bill_detail || undefined,
66 struk: data.struk || undefined, 66 struk: data.struk || undefined,
67 }; 67 };
68 68
69 if (data.command === 'INQUIRY' && data.amount_to_charge) { 69 if (data.command === 'INQUIRY' && data.amount_to_charge) {
70 params.amount_to_charge = data.amount_to_charge; 70 params.amount_to_charge = data.amount_to_charge;
71 } 71 }
72 72
73 const isPostpaid = ['INQUIRY', 'PAY'].indexOf(data.command) >= 0; 73 const isPostpaid = ['INQUIRY', 'PAY'].indexOf(data.command) >= 0;
74 const isHttpPost = isPostpaid; 74 const isHttpPost = isPostpaid;
75 75
76 try { 76 try {
77 const webhookType = 'KOMODO-CENTER-HTTPGETX.CORE-CALLBACK'; 77 const webhookType = 'KOMODO-CENTER-HTTPGETX.CORE-CALLBACK';
78 webhookSender(xid, webhookType, params);
79 } catch (e) {
80 logger.warn(`${MODULE_NAME} 1E2BF2CD: Exception calling webhookSender`, {
81 xid,
82 });
83 }
78 webhookSender(xid, webhookType, params); 84
79 } catch (e) { 85 if (!data.reverse_url) {
80 logger.warn(`${MODULE_NAME} 1E2BF2CD: Exception calling webhookSender`, { 86 logger.verbose(`${MODULE_NAME} C4FF18FB: Ignoring missing reverse url`, {
81 xid, 87 xid,
82 }); 88 dataFromCore: data,
83 } 89 });
84 90
85 if (!data.reverse_url) { 91 return;
86 logger.verbose(`${MODULE_NAME} C4FF18FB: Ignoring missing reverse url`, { 92 }
87 xid, 93
88 dataFromCore: data, 94 const endpointUrl = isHttpPost ? data.reverse_url : urlConcatQs(data.reverse_url, params);
89 }); 95
90 96 logger.info(`${MODULE_NAME} 8B6A4CEC: Sending to PARTNER`, {
91 return; 97 xid,
92 } 98 retry: retry || 0,
93 99 isPostpaid,
94 const endpointUrl = isHttpPost ? data.reverse_url : urlConcatQs(data.reverse_url, params); 100 isHttpPost,
95 101 endpointUrl,
96 logger.info(`${MODULE_NAME} 8B6A4CEC: Sending to PARTNER`, { 102 });
97 xid, 103
98 retry: retry || 0, 104 let responseToDump;
99 isPostpaid, 105 let errorResponseToDump;
100 isHttpPost, 106
101 endpointUrl, 107 try {
102 }); 108 const response = isHttpPost
103 109 ? await axios.post(data.reverse_url, params, {
104 let responseToDump; 110 timeout: HTTP_TIMEOUT,
105 let errorResponseToDump; 111 headers: axiosHeaders,
106 112 })
107 try { 113 : await axios.get(data.reverse_url, {
108 const response = isHttpPost 114 params,
109 ? await axios.post(data.reverse_url, params, { 115 timeout: HTTP_TIMEOUT,
110 timeout: HTTP_TIMEOUT, 116 headers: axiosHeaders,
111 headers: axiosHeaders, 117 });
112 }) 118
113 : await axios.get(data.reverse_url, { 119 responseToDump = response;
114 params, 120
115 timeout: HTTP_TIMEOUT, 121 matrix.callback_sender.sent += 1;
116 headers: axiosHeaders, 122 matrix.callback_sender.active_count += 1;
117 }); 123 matrix.callback_sender.active_sending[xid] = {
118 124 ts: new Date(),
119 responseToDump = response; 125 trxId: data.trx_id,
120 126 reverseUrl: data.reverse_url,
121 matrix.callback_sender.sent += 1; 127 };
122 matrix.callback_sender.active_count += 1; 128
123 matrix.callback_sender.active_sending[xid] = { 129 if (isPostpaid) {
124 ts: new Date(), 130 matrix.callback_sender.sent_using_post += 1;
125 trxId: data.trx_id, 131 } else {
126 reverseUrl: data.reverse_url, 132 matrix.callback_sender.sent_using_get += 1;
127 }; 133 }
128 134
129 if (isPostpaid) { 135 logger.info(`${MODULE_NAME} 3641FBD7: Has been sent to PARTNER successfully`, {
130 matrix.callback_sender.sent_using_post += 1; 136 xid,
131 } else { 137 retry,
132 matrix.callback_sender.sent_using_get += 1; 138 httpStatus: response.status,
133 } 139 responseBody: response && response.data,
134 140 });
135 logger.info(`${MODULE_NAME} 3641FBD7: Has been sent to PARTNER successfully`, { 141 } catch (e) {
136 xid, 142 matrix.callback_sender.sent_failed += 1;
137 retry, 143 matrix.callback_sender.last_error = {
138 httpStatus: response.status, 144 xid,
139 responseBody: response && response.data, 145 ts: new Date(),
140 }); 146 eCode: e.code,
141 } catch (e) { 147 eMessage: e.message,
142 matrix.callback_sender.sent_failed += 1; 148 trxId: data.trx_id,
143 matrix.callback_sender.last_error = { 149 reverseUrl: data.reverse_url,
144 xid, 150 httpStatus: e.response && e.response.status,
145 ts: new Date(), 151 responseBody: e.response && e.response.data,
146 eCode: e.code, 152 };
147 eMessage: e.message, 153
148 trxId: data.trx_id, 154 responseToDump = e.response && e.response.data;
149 reverseUrl: data.reverse_url, 155 errorResponseToDump = e;
150 httpStatus: e.response && e.response.status, 156
151 responseBody: e.response && e.response.data, 157 logger.warn(`${MODULE_NAME} A1EC9E70: Failed on sending to PARTNER`, {
152 }; 158 xid,
153 159 retry,
154 responseToDump = e.response && e.response.data; 160 maxRetry: MAX_RETRY,
155 errorResponseToDump = e; 161 errCode: e.code,
156 162 errMessage: e.message,
157 logger.warn(`${MODULE_NAME} A1EC9E70: Failed on sending to PARTNER`, { 163 reverseUrl: data.reverse_url,
158 xid, 164 endpointUrl,
159 retry, 165 httpStatus: e.response && e.response.status,
160 maxRetry: MAX_RETRY, 166 responseBody: e.response && e.response.data,
161 errCode: e.code, 167 });
162 errMessage: e.message, 168
163 reverseUrl: data.reverse_url, 169 if (e.response && e.response.status) {
164 endpointUrl, 170 logger.verbose(`${MODULE_NAME} 10AE785C: Skip retry on http status presence`, {
165 httpStatus: e.response && e.response.status, 171 xid,
166 responseBody: e.response && e.response.data, 172 httpStatus: e.response && e.response.status,
167 }); 173 });
168 174 return;
169 if (e.response && e.response.status) { 175 }
170 logger.verbose(`${MODULE_NAME} 10AE785C: Skip retry on http status presence`, { 176
171 xid, 177 if ((retry || 0) < MAX_RETRY) {
172 httpStatus: e.response && e.response.status, 178 await sleep(SLEEP_BEFORE_RETRY_MS);
173 }); 179
174 return; 180 logger.verbose(`${MODULE_NAME} D8958695: Going to retry sending CORE-CALLBACK TO PARTNER`, {
175 } 181 xid,
176 182 retried: retry,
177 if ((retry || 0) < MAX_RETRY) { 183 sleepTime: SLEEP_BEFORE_RETRY_MS,
178 await sleep(SLEEP_BEFORE_RETRY_MS); 184 });
179 185
180 logger.verbose(`${MODULE_NAME} D8958695: Going to retry sending CORE-CALLBACK TO PARTNER`, { 186 sender(data, xid, (retry || 0) + 1);
181 xid, 187 }
182 retried: retry, 188 } finally {
183 sleepTime: SLEEP_BEFORE_RETRY_MS, 189 matrix.callback_sender.active_count -= 1;
184 }); 190 if (matrix.callback_sender.active_sending[xid]) {
185 191 delete matrix.callback_sender.active_sending[xid];
186 sender(data, xid, (retry || 0) + 1); 192 }
187 } 193
188 } finally { 194 dumper(
189 matrix.callback_sender.active_count -= 1; 195 xid,
190 if (matrix.callback_sender.active_sending[xid]) { 196 isHttpPost ? 'POST' : 'GET',
191 delete matrix.callback_sender.active_sending[xid]; 197 endpointUrl,
192 } 198 params,
193 199 responseToDump,
194 dumper( 200 errorResponseToDump,
195 xid, 201 );
196 isHttpPost ? 'POST' : 'GET', 202 }
197 endpointUrl, 203 };
198 params, 204
199 responseToDump, 205 module.exports = sender;
200 errorResponseToDump, 206
lib/webhook-sender.js
1 const MODULE_NAME = 'WEBHOOK-SENDER'; 1 const MODULE_NAME = 'WEBHOOK-SENDER';
2 2
3 const axios = require('axios'); 3 const axios = require('axios');
4 const moment = require('moment'); 4 const moment = require('moment');
5 const fs = require('fs'); 5 const fs = require('fs');
6 const path = require('path'); 6 const path = require('path');
7 const stringify = require('json-stringify-pretty-compact'); 7 const stringify = require('json-stringify-pretty-compact');
8 const config = require('komodo-sdk/config'); 8 const config = require('komodo-sdk/config');
9 const logger = require('tektrans-logger'); 9 const logger = require('tektrans-logger');
10 10
11 const DEFAULT_MAX_RETRY = 10; 11 const DEFAULT_MAX_RETRY = 10;
12 const DEFAULT_SLEEP_BEFORE_RETRY_MS = 10 * 1000; 12 const DEFAULT_SLEEP_BEFORE_RETRY_MS = 10 * 1000;
13 13
14 const maxRetry = Number(config.webhook && config.webhook.max_retry) 14 const maxRetry = Number(config.webhook && config.webhook.max_retry)
15 || DEFAULT_MAX_RETRY; 15 || DEFAULT_MAX_RETRY;
16 const sleepBeforeRetryMs = Number(config.webhook && config.webhook.sleep_before_retry_ms) 16 const sleepBeforeRetryMs = Number(config.webhook && config.webhook.sleep_before_retry_ms)
17 || DEFAULT_SLEEP_BEFORE_RETRY_MS; 17 || DEFAULT_SLEEP_BEFORE_RETRY_MS;
18 18
19 const baseDumpDir = path.join('dump', 'webhook-sender'); 19 const baseDumpDir = path.join('dump', 'webhook-sender');
20 if (!fs.existsSync(baseDumpDir)) { 20 if (!fs.existsSync(baseDumpDir)) {
21 fs.mkdirSync(baseDumpDir, { recursive: true }); 21 fs.mkdirSync(baseDumpDir, { recursive: true });
22 } 22 }
23 const lastDumpFileName = path.join(baseDumpDir, 'last'); 23 const lastDumpFileName = path.join(baseDumpDir, 'last');
24 24
25 const sleepMs = (ms) => new Promise((resolve) => { 25 const sleepMs = (ms) => new Promise((resolve) => {
26 setTimeout(() => { 26 setTimeout(() => {
27 resolve(); 27 resolve();
28 }, ms); 28 }, ms);
29 }); 29 });
30 30
31 const dumper = async (xid, webhookType, body) => { 31 const dumper = async (xid, webhookType, body) => {
32 if (!config.webhook || !config.webhook.dump) { 32 if (!config.webhook || !config.webhook.dump) {
33 return; 33 return;
34 } 34 }
35 35
36 await fs.promises.writeFile( 36 try {
37 path.join(baseDumpDir, [moment().format('YYYYMMDD-HHmmssSSS'), xid].join('_')), 37 await fs.promises.writeFile(
38 stringify({ webhookType, body }), 38 path.join(baseDumpDir, [moment().format('YYYYMMDD-HHmmssSSS'), xid].join('_')),
39 ); 39 stringify({ webhookType, body }),
40 40 );
41 await fs.promises.writeFile( 41
42 lastDumpFileName, 42 await fs.promises.writeFile(
43 stringify({ webhookType, body }), 43 lastDumpFileName,
44 ); 44 stringify({ webhookType, body }),
45 );
46 } catch (e) {
47 logger.warn(`${MODULE_NAME} D3EF00D9: Exception on dumper`, {
48 xid,
49 eCode: e.code,
50 eMessage: e.message || e.toString(),
51 });
52 }
45 }; 53 };
46 54
47 const sender = async (xid, webhookType, body, retry) => { 55 const sender = async (xid, webhookType, body, retry) => {
48 if (!config.webhook || !config.webhook.url) { 56 if (!config.webhook || !config.webhook.url) {
49 return; 57 return;
50 } 58 }
51 59
52 try { 60 try {
53 logger.verbose(`${MODULE_NAME} 2CA59ED3: Sending webhook`, { 61 logger.verbose(`${MODULE_NAME} 2CA59ED3: Sending webhook`, {
54 xid, 62 xid,
55 webhookType, 63 webhookType,
56 partner: config.webhook.url, 64 partner: config.webhook.url,
57 trxId: body.transaction_id, 65 trxId: body.transaction_id,
58 request_id: body.request_id, 66 request_id: body.request_id,
59 }); 67 });
60 68
61 axios.post(config.listener.partner.webhook, { 69 axios.post(config.listener.partner.webhook, {
62 webhookType, 70 webhookType,
63 body, 71 body,
64 }); 72 });
65 73
66 await dumper(xid, webhookType, body); 74 await dumper(xid, webhookType, body);
67 75
68 logger.verbose(`${MODULE_NAME} 50BE8D98: Webhook sent`, { 76 logger.verbose(`${MODULE_NAME} 50BE8D98: Webhook sent`, {
69 xid, 77 xid,
70 webhookType, 78 webhookType,
71 partner: config.listener.partner.webhook, 79 partner: config.listener.partner.webhook,
72 }); 80 });
73 } catch (e) { 81 } catch (e) {
74 logger.warn(`${MODULE_NAME} ECC37ECA: Exception on calling webhook`, { 82 logger.warn(`${MODULE_NAME} ECC37ECA: Exception on calling webhook`, {
75 xid, 83 xid,
84 httpStatusCode: e.response && e.response.status,
76 httpStatusCode: e.response && e.response.status, 85 eCode: e.code,
77 eCode: e.code, 86 eMessage: e.message || e.toString(),
78 eMessage: e.message || e.toString(), 87 retried: retry || 0,
79 retried: retry || 0, 88 maxRetry,
80 maxRetry, 89 });
81 }); 90
82 91 if ((retry || 0) >= maxRetry) {
83 if ((retry || 0) >= maxRetry) { 92 logger.warn(`${MODULE_NAME} 4A60B406: Max retry exceeded`, {
84 logger.warn(`${MODULE_NAME} 4A60B406: Max retry exceeded`, { 93 xid,
85 xid, 94 });
86 }); 95
87 96 return;
88 return; 97 }
89 } 98
90 99 await sleepMs(sleepBeforeRetryMs);
91 await sleepMs(sleepBeforeRetryMs); 100 await sender(xid, webhookType, body, (retry || 0) + 1);
92 sender(xid, webhookType, body, (retry || 0) + 1); 101 }
93 } 102 };
94 }; 103 module.exports = sender;
95 module.exports = sender; 104