Commit 485b7af4bbe868c6f3255cad379fecc3db69dae6

Authored by Adhidarma Hadiwinoto
1 parent 5e27716d8e
Exists in master

Debug webhooksender dari core-response

Showing 2 changed files with 9 additions and 2 deletions 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 const webhookType = 'KOMODO-CENTER-HTTPGETX.CORE-CALLBACK'; 76 try {
77 webhookSender(xid, webhookType, params); 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 84
79 if (!data.reverse_url) { 85 if (!data.reverse_url) {
80 logger.verbose(`${MODULE_NAME} C4FF18FB: Ignoring missing reverse url`, { 86 logger.verbose(`${MODULE_NAME} C4FF18FB: Ignoring missing reverse url`, {
81 xid, 87 xid,
82 dataFromCore: data, 88 dataFromCore: data,
83 }); 89 });
84 90
85 return; 91 return;
86 } 92 }
87 93
88 const endpointUrl = isHttpPost ? data.reverse_url : urlConcatQs(data.reverse_url, params); 94 const endpointUrl = isHttpPost ? data.reverse_url : urlConcatQs(data.reverse_url, params);
89 95
90 logger.info(`${MODULE_NAME} 8B6A4CEC: Sending to PARTNER`, { 96 logger.info(`${MODULE_NAME} 8B6A4CEC: Sending to PARTNER`, {
91 xid, 97 xid,
92 retry: retry || 0, 98 retry: retry || 0,
93 isPostpaid, 99 isPostpaid,
94 isHttpPost, 100 isHttpPost,
95 endpointUrl, 101 endpointUrl,
96 }); 102 });
97 103
98 let responseToDump; 104 let responseToDump;
99 let errorResponseToDump; 105 let errorResponseToDump;
100 106
101 try { 107 try {
102 const response = isHttpPost 108 const response = isHttpPost
103 ? await axios.post(data.reverse_url, params, { 109 ? await axios.post(data.reverse_url, params, {
104 timeout: HTTP_TIMEOUT, 110 timeout: HTTP_TIMEOUT,
105 headers: axiosHeaders, 111 headers: axiosHeaders,
106 }) 112 })
107 : await axios.get(data.reverse_url, { 113 : await axios.get(data.reverse_url, {
108 params, 114 params,
109 timeout: HTTP_TIMEOUT, 115 timeout: HTTP_TIMEOUT,
110 headers: axiosHeaders, 116 headers: axiosHeaders,
111 }); 117 });
112 118
113 responseToDump = response; 119 responseToDump = response;
114 120
115 matrix.callback_sender.sent += 1; 121 matrix.callback_sender.sent += 1;
116 matrix.callback_sender.active_count += 1; 122 matrix.callback_sender.active_count += 1;
117 matrix.callback_sender.active_sending[xid] = { 123 matrix.callback_sender.active_sending[xid] = {
118 ts: new Date(), 124 ts: new Date(),
119 trxId: data.trx_id, 125 trxId: data.trx_id,
120 reverseUrl: data.reverse_url, 126 reverseUrl: data.reverse_url,
121 }; 127 };
122 128
123 if (isPostpaid) { 129 if (isPostpaid) {
124 matrix.callback_sender.sent_using_post += 1; 130 matrix.callback_sender.sent_using_post += 1;
125 } else { 131 } else {
126 matrix.callback_sender.sent_using_get += 1; 132 matrix.callback_sender.sent_using_get += 1;
127 } 133 }
128 134
129 logger.info(`${MODULE_NAME} 3641FBD7: Has been sent to PARTNER successfully`, { 135 logger.info(`${MODULE_NAME} 3641FBD7: Has been sent to PARTNER successfully`, {
130 xid, 136 xid,
131 retry, 137 retry,
132 httpStatus: response.status, 138 httpStatus: response.status,
133 responseBody: response && response.data, 139 responseBody: response && response.data,
134 }); 140 });
135 } catch (e) { 141 } catch (e) {
136 matrix.callback_sender.sent_failed += 1; 142 matrix.callback_sender.sent_failed += 1;
137 matrix.callback_sender.last_error = { 143 matrix.callback_sender.last_error = {
138 xid, 144 xid,
139 ts: new Date(), 145 ts: new Date(),
140 eCode: e.code, 146 eCode: e.code,
141 eMessage: e.message, 147 eMessage: e.message,
142 trxId: data.trx_id, 148 trxId: data.trx_id,
143 reverseUrl: data.reverse_url, 149 reverseUrl: data.reverse_url,
144 httpStatus: e.response && e.response.status, 150 httpStatus: e.response && e.response.status,
145 responseBody: e.response && e.response.data, 151 responseBody: e.response && e.response.data,
146 }; 152 };
147 153
148 responseToDump = e.response && e.response.data; 154 responseToDump = e.response && e.response.data;
149 errorResponseToDump = e; 155 errorResponseToDump = e;
150 156
151 logger.warn(`${MODULE_NAME} A1EC9E70: Failed on sending to PARTNER`, { 157 logger.warn(`${MODULE_NAME} A1EC9E70: Failed on sending to PARTNER`, {
152 xid, 158 xid,
153 retry, 159 retry,
154 maxRetry: MAX_RETRY, 160 maxRetry: MAX_RETRY,
155 errCode: e.code, 161 errCode: e.code,
156 errMessage: e.message, 162 errMessage: e.message,
157 reverseUrl: data.reverse_url, 163 reverseUrl: data.reverse_url,
158 endpointUrl, 164 endpointUrl,
159 httpStatus: e.response && e.response.status, 165 httpStatus: e.response && e.response.status,
160 responseBody: e.response && e.response.data, 166 responseBody: e.response && e.response.data,
161 }); 167 });
162 168
163 if (e.response && e.response.status) { 169 if (e.response && e.response.status) {
164 logger.verbose(`${MODULE_NAME} 10AE785C: Skip retry on http status presence`, { 170 logger.verbose(`${MODULE_NAME} 10AE785C: Skip retry on http status presence`, {
165 xid, 171 xid,
166 httpStatus: e.response && e.response.status, 172 httpStatus: e.response && e.response.status,
167 }); 173 });
168 return; 174 return;
169 } 175 }
170 176
171 if ((retry || 0) < MAX_RETRY) { 177 if ((retry || 0) < MAX_RETRY) {
172 await sleep(SLEEP_BEFORE_RETRY_MS); 178 await sleep(SLEEP_BEFORE_RETRY_MS);
173 179
174 logger.verbose(`${MODULE_NAME} D8958695: Going to retry sending CORE-CALLBACK TO PARTNER`, { 180 logger.verbose(`${MODULE_NAME} D8958695: Going to retry sending CORE-CALLBACK TO PARTNER`, {
175 xid, 181 xid,
176 retried: retry, 182 retried: retry,
177 sleepTime: SLEEP_BEFORE_RETRY_MS, 183 sleepTime: SLEEP_BEFORE_RETRY_MS,
178 }); 184 });
179 185
180 sender(data, xid, (retry || 0) + 1); 186 sender(data, xid, (retry || 0) + 1);
181 } 187 }
182 } finally { 188 } finally {
183 matrix.callback_sender.active_count -= 1; 189 matrix.callback_sender.active_count -= 1;
184 if (matrix.callback_sender.active_sending[xid]) { 190 if (matrix.callback_sender.active_sending[xid]) {
185 delete matrix.callback_sender.active_sending[xid]; 191 delete matrix.callback_sender.active_sending[xid];
186 } 192 }
187 193
188 dumper( 194 dumper(
189 xid, 195 xid,
190 isHttpPost ? 'POST' : 'GET', 196 isHttpPost ? 'POST' : 'GET',
191 endpointUrl, 197 endpointUrl,
192 params, 198 params,
193 responseToDump, 199 responseToDump,
194 errorResponseToDump, 200 errorResponseToDump,
195 ); 201 );
196 } 202 }
197 }; 203 };
198 204
199 module.exports = sender; 205 module.exports = sender;
200 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 await fs.promises.writeFile(
37 path.join(baseDumpDir, [moment().format('YYYYMMDD-HHmmssSSS'), xid].join('_')), 37 path.join(baseDumpDir, [moment().format('YYYYMMDD-HHmmssSSS'), xid].join('_')),
38 stringify({ webhookType, body }), 38 stringify({ webhookType, body }),
39 ); 39 );
40 40
41 await fs.promises.writeFile( 41 await fs.promises.writeFile(
42 lastDumpFileName, 42 lastDumpFileName,
43 stringify({ webhookType, body }), 43 stringify({ webhookType, body }),
44 ); 44 );
45 }; 45 };
46 46
47 const sender = async (xid, webhookType, body, retry) => { 47 const sender = async (xid, webhookType, body, retry) => {
48 if (!config.webhook || !config.webhook.url) { 48 if (!config.webhook || !config.webhook.url) {
49 return; 49 return;
50 } 50 }
51 51
52 try { 52 try {
53 logger.verbose(`${MODULE_NAME} 2CA59ED3: Sending webhook`, { 53 logger.verbose(`${MODULE_NAME} 2CA59ED3: Sending webhook`, {
54 xid, 54 xid,
55 webhookType, 55 webhookType,
56 partner: config.webhook.url, 56 partner: config.webhook.url,
57 trxId: body.transaction_id, 57 trxId: body.transaction_id,
58 request_id: body.request_id, 58 request_id: body.request_id,
59 }); 59 });
60 60
61 axios.post(config.listener.partner.webhook, { 61 axios.post(config.listener.partner.webhook, {
62 webhookType, 62 webhookType,
63 body, 63 body,
64 }); 64 });
65 65
66 await dumper(xid, webhookType, body); 66 await dumper(xid, webhookType, body);
67 67
68 logger.verbose(`${MODULE_NAME} 50BE8D98: Webhook sent`, { 68 logger.verbose(`${MODULE_NAME} 50BE8D98: Webhook sent`, {
69 xid, 69 xid,
70 webhookType, 70 webhookType,
71 partner: config.listener.partner.webhook, 71 partner: config.listener.partner.webhook,
72 }); 72 });
73 } catch (e) { 73 } catch (e) {
74 logger.warn(`${MODULE_NAME} ECC37ECA: Exception on calling webhook`, { 74 logger.warn(`${MODULE_NAME} ECC37ECA: Exception on calling webhook`, {
75 xid, 75 xid,
76 httpStatusCode: e.response && e.response.status,
76 eCode: e.code, 77 eCode: e.code,
77 eMessage: e.message || e.toString(), 78 eMessage: e.message || e.toString(),
78 retried: retry || 0, 79 retried: retry || 0,
79 maxRetry, 80 maxRetry,
80 }); 81 });
81 82
82 if ((retry || 0) >= maxRetry) { 83 if ((retry || 0) >= maxRetry) {
83 logger.warn(`${MODULE_NAME} 4A60B406: Max retry exceeded`, { 84 logger.warn(`${MODULE_NAME} 4A60B406: Max retry exceeded`, {
84 xid, 85 xid,
85 }); 86 });
86 87
87 return; 88 return;
88 } 89 }
89 90
90 await sleepMs(sleepBeforeRetryMs); 91 await sleepMs(sleepBeforeRetryMs);
91 sender(xid, webhookType, body, (retry || 0) + 1); 92 sender(xid, webhookType, body, (retry || 0) + 1);
92 } 93 }
93 }; 94 };
94 module.exports = sender; 95 module.exports = sender;
95 96