Commit 4acfa7529325b3040e4247cdef9f01df4d3ed850
1 parent
7e92010a8b
Exists in
master
ready to test
Showing 4 changed files with 281 additions and 1 deletions Inline Diff
index.js
1 | "use strict"; | 1 | "use strict"; |
2 | 2 | ||
3 | var aaa = require('sate24/aaa.js'); | 3 | var aaa = require('sate24/aaa.js'); |
4 | var expresso = require('sate24-expresso'); | 4 | var expresso = require('sate24-expresso'); |
5 | var logger = require('sate24/logger.js').start(); | 5 | var logger = require('sate24/logger.js').start(); |
6 | var HttpServer = require('sate24/httpserver.js'); | 6 | var HttpServer = require('sate24/httpserver.js'); |
7 | var partner = require('./partner-simplepay.js'); | 7 | var partner = require('./partner-bnisp.js'); |
8 | 8 | ||
9 | var fs = require('fs'); | 9 | var fs = require('fs'); |
10 | var ini = require('ini'); | 10 | var ini = require('ini'); |
11 | var config = ini.parse(fs.readFileSync(__dirname + '/config.ini', 'utf-8')); | 11 | var config = ini.parse(fs.readFileSync(__dirname + '/config.ini', 'utf-8')); |
12 | 12 | ||
13 | process.chdir(__dirname); | 13 | process.chdir(__dirname); |
14 | 14 | ||
15 | var logDirectory = __dirname + '/logs'; | 15 | var logDirectory = __dirname + '/logs'; |
16 | fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory); | 16 | fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory); |
17 | 17 | ||
18 | var matrix = aaa.prepareMatrix(); | 18 | var matrix = aaa.prepareMatrix(); |
19 | 19 | ||
20 | var options = { | 20 | var options = { |
21 | 'aaa': aaa, | 21 | 'aaa': aaa, |
22 | 'logger': logger, | 22 | 'logger': logger, |
23 | 'config': config, | 23 | 'config': config, |
24 | 'matrix': matrix, | 24 | 'matrix': matrix, |
25 | } | 25 | } |
26 | 26 | ||
27 | var httpServer = HttpServer.start(config, options); | 27 | var httpServer = HttpServer.start(config, options); |
28 | 28 | ||
29 | partner.start(options); | 29 | partner.start(options); |
30 | aaa.start(config, partner, options); | 30 | aaa.start(config, partner, options); |
31 | expresso.start(options); | 31 | expresso.start(options); |
32 | 32 |
package.json
1 | { | 1 | { |
2 | "name": "sate24-to-bnisyariah-prosesor", | 2 | "name": "sate24-to-bnisyariah-prosesor", |
3 | "version": "1.0.0", | 3 | "version": "1.0.0", |
4 | "description": "ST24 to BNI Syariah Prosesor", | 4 | "description": "ST24 to BNI Syariah Prosesor", |
5 | "main": "index.js", | 5 | "main": "index.js", |
6 | "scripts": { | 6 | "scripts": { |
7 | "test": "mocha" | 7 | "test": "mocha" |
8 | }, | 8 | }, |
9 | "repository": { | 9 | "repository": { |
10 | "type": "git", | 10 | "type": "git", |
11 | "url": "git@gitlab.kodesumber.com:reload97/sate24-to-bnisyariah-prosesor.git" | 11 | "url": "git@gitlab.kodesumber.com:reload97/sate24-to-bnisyariah-prosesor.git" |
12 | }, | 12 | }, |
13 | "keywords": [ | 13 | "keywords": [ |
14 | "st24", | 14 | "st24", |
15 | "ppob", | 15 | "ppob", |
16 | "reload97", | 16 | "reload97", |
17 | "r97", | 17 | "r97", |
18 | "h2h" | 18 | "h2h" |
19 | ], | 19 | ], |
20 | "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", | 20 | "author": "Adhidarma Hadiwinoto <me@adhisimon.org>", |
21 | "license": "ISC", | 21 | "license": "ISC", |
22 | "dependencies": { | 22 | "dependencies": { |
23 | "request": "^2.81.0", | ||
23 | "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git", | 24 | "sate24": "git+http://gitlab.kodesumber.com/reload97/node-sate24.git", |
24 | "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git" | 25 | "sate24-expresso": "git+http://gitlab.kodesumber.com/reload97/sate24-expresso.git" |
26 | }, | ||
27 | "devDependencies": { | ||
28 | "mocha": "^3.4.2", | ||
29 | "should": "^11.2.1" | ||
25 | } | 30 | } |
26 | } | 31 | } |
27 | 32 |
partner-bnisp.js
File was created | 1 | "use strict"; | |
2 | |||
3 | const http = require('http'); | ||
4 | http.globalAgent.maxSockets = Infinity; | ||
5 | |||
6 | const request = require('request'); | ||
7 | |||
8 | var config; | ||
9 | var aaa; | ||
10 | var logger; | ||
11 | |||
12 | function start(options) { | ||
13 | if (!options) { | ||
14 | console.log('Undefined options, terminating....'); | ||
15 | process.exit(1); | ||
16 | } | ||
17 | |||
18 | if (options.config) { | ||
19 | config = options.config; | ||
20 | } else { | ||
21 | console.log('Undefined options.config, terminating....') | ||
22 | process.exit(1); | ||
23 | } | ||
24 | |||
25 | if (options.aaa) { | ||
26 | aaa = options.aaa; | ||
27 | } else { | ||
28 | console.log('Undefined options.aaa, terminating....') | ||
29 | process.exit(1); | ||
30 | } | ||
31 | |||
32 | if (options && options.logger) { | ||
33 | logger = options.logger; | ||
34 | } else { | ||
35 | console.log('Undefined options.logger, terminating....') | ||
36 | process.exit(1); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | function callbackReport(requestId, rc, message, options) { | ||
41 | aaa.callbackReportWithPushToMongoDb(requestId, rc, message); | ||
42 | } | ||
43 | |||
44 | function topupRequest(task) { | ||
45 | aaa.insertTaskToMongoDb(task); | ||
46 | _hitTopup(task); | ||
47 | } | ||
48 | |||
49 | function _hitTopup(task, isCheckStatus) { | ||
50 | |||
51 | const remoteProduct = task.remoteProduct.split('/'); | ||
52 | if (remoteProduct.length < 4) { | ||
53 | callbackReport(task.requestId, '40', 'INTERNAL_MSG: Invalid remoteProduct', task) | ||
54 | return; | ||
55 | } | ||
56 | |||
57 | let pathParams = { | ||
58 | request_type: "purchase", | ||
59 | noid: config.h2h_out.noid, | ||
60 | token: config.h2h_out.token, | ||
61 | product: task.remoteProduct[0], | ||
62 | product_type: task.remoteProduct[1], | ||
63 | id_pel: task.destination, | ||
64 | nominal: task.remoteProduct[2], | ||
65 | admin: task.remoteProduct[3], | ||
66 | trace_id: task.requestId | ||
67 | } | ||
68 | |||
69 | const requestOptions = { | ||
70 | url: config.h2h_out.partner + createUrlPath(pathParams) | ||
71 | } | ||
72 | |||
73 | logger.verbose('Requeting to partner', {requestOptions: requestOptions}); | ||
74 | request(requestOptions, function(error, response, body) { | ||
75 | if (error) { | ||
76 | let rc = '68'; | ||
77 | |||
78 | if (!isCheckStatus && (error.syscall == 'connect')) { | ||
79 | rc = '91'; | ||
80 | } | ||
81 | |||
82 | logger.warn('Error requesting to partner', {task: task, rc: rc, error: error}); | ||
83 | callbackReport(task.requestId, rc, 'INTERNAL_MSG: Error requesting to partner. ' + error, {task: task}); | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | if (response.statusCode != 200) { | ||
88 | let rc = '68'; | ||
89 | |||
90 | logger.warn('HTTP status code is not 200', {task: task, http_status_code: response.statusCode}); | ||
91 | callbackReport(task.requestId, rc, 'INTERNAL_MSG: HTTP status code ' + response.statusCode, {task: task}); | ||
92 | return; | ||
93 | } | ||
94 | |||
95 | if (!body) { | ||
96 | let rc = '68'; | ||
97 | |||
98 | logger.warn('Error processing response body', {task: task, responseBody: body}); | ||
99 | callbackReport(task.requestId, rc, 'INTERNAL_MSG: Error processing response body', {task: task}); | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | const responseData = parseResponseBody(body); | ||
104 | const data = responseDataProcessor(responseData); | ||
105 | |||
106 | callbackReport(task.requestId, data.rc, data.combinedMessage, {task: task}); | ||
107 | }) | ||
108 | |||
109 | } | ||
110 | |||
111 | function createUrlPath(options) { | ||
112 | let urlPath = [ | ||
113 | "get", | ||
114 | options.request_type || "purchase", | ||
115 | "json", | ||
116 | options.noid, | ||
117 | options.token, | ||
118 | options.product, | ||
119 | options.product_type, | ||
120 | options.id_pel, | ||
121 | options.nominal || 0, | ||
122 | options.admin || 0, | ||
123 | options.trace_id | ||
124 | ].join('/'); | ||
125 | |||
126 | return '/' + urlPath; | ||
127 | } | ||
128 | |||
129 | function parseResponseBody(body) { | ||
130 | let data; | ||
131 | |||
132 | try { | ||
133 | data = JSON.parse(body); | ||
134 | } | ||
135 | catch(e) { | ||
136 | if (logger) { | ||
137 | logger.warn('Exception on parsing result body: ' + e); | ||
138 | return; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | return data; | ||
143 | } | ||
144 | |||
145 | function responseDataProcessor(responseData) { | ||
146 | let retval = { | ||
147 | rc: '68', | ||
148 | sn: '', | ||
149 | responseMessage: '', | ||
150 | amount: 0, | ||
151 | balance: 0, | ||
152 | ts: '', | ||
153 | product: '', | ||
154 | productType: '' | ||
155 | } | ||
156 | |||
157 | let combinedMessage = []; | ||
158 | |||
159 | if (responseData.response_code == '0000') { | ||
160 | retval.rc = '00'; | ||
161 | } | ||
162 | else if (['9990','0099','0068','0063','0005','9997','9999','9998','9990','0600','1003'].indexOf(responseData.response_code) >= 0) { | ||
163 | retval.rc = '68'; | ||
164 | } | ||
165 | else { | ||
166 | retval.rc = '40'; | ||
167 | } | ||
168 | |||
169 | if (responseData.response_code) { | ||
170 | combinedMessage.push(responseData.response_code); | ||
171 | } | ||
172 | |||
173 | if (responseData.product) { | ||
174 | combinedMessage.push(responseData.product) | ||
175 | } | ||
176 | |||
177 | if (responseData.produk_tipe) { | ||
178 | combinedMessage.push(responseData.produk_tipe) | ||
179 | } | ||
180 | |||
181 | if (responseData.idpel) { | ||
182 | combinedMessage.push(responseData.idpel) | ||
183 | } | ||
184 | |||
185 | retval.responseMessage = responseData.response_message || ''; | ||
186 | if (retval.responseMessage) { | ||
187 | combinedMessage.push(retval.responseMessage); | ||
188 | } | ||
189 | |||
190 | if (responseData.detail) { | ||
191 | retval.sn = responseData.detail.voucherSerialNumber || ''; | ||
192 | } | ||
193 | |||
194 | retval.amount = responseData.amount || 0; | ||
195 | combinedMessage.push('amount: ' + retval.amount); | ||
196 | |||
197 | retval.balance = responseData.saldo || 0; | ||
198 | combinedMessage.push('balance: ' + retval.balance); | ||
199 | |||
200 | retval.ts = responseData.waktu || ''; | ||
201 | combinedMessage.push(retval.ts); | ||
202 | |||
203 | retval.loadTime = responseData.loadTime || ''; | ||
204 | combinedMessage.push('load time: ' + retval.loadTime); | ||
205 | |||
206 | retval.combinedMessage = combinedMessage.join(' '); | ||
207 | |||
208 | if (retval.sn) { | ||
209 | retval.combinedMessage = 'SN=' + retval.sn + '; ' + retval.combinedMessage; | ||
210 | } | ||
211 | |||
212 | return retval; | ||
213 | } | ||
214 | |||
215 | exports.createUrlPath = createUrlPath; | ||
216 | exports.parseResponseBody = parseResponseBody; | ||
217 | exports.responseDataProcessor = responseDataProcessor; | ||
218 | exports.topupRequest = topupRequest; | ||
219 |
test.js
File was created | 1 | "use strict"; | |
2 | |||
3 | const should = require("should"); | ||
4 | const partner = require("./partner-bnisp"); | ||
5 | |||
6 | describe("#partner", function() { | ||
7 | describe("#createUrlPath", function() { | ||
8 | |||
9 | it('should return correct url path based on example', function() { | ||
10 | let options = { | ||
11 | noid: 1002003, | ||
12 | token: 'CIPY6t6ruy5UuGG0PCqu', | ||
13 | product: 'PULSA', | ||
14 | product_type: 'THREE5000', | ||
15 | id_pel: '0895385381299', | ||
16 | trace_id: 16 | ||
17 | } | ||
18 | |||
19 | partner.createUrlPath(options).should.equal('/get/purchase/json/1002003/CIPY6t6ruy5UuGG0PCqu/PULSA/THREE5000/0895385381299/0/0/16') | ||
20 | }) | ||
21 | |||
22 | }) | ||
23 | |||
24 | const responseBody = '{"trxId":"064024","response_code":"0000","response_message":"SUKSES","detail":{"jmlTagihan":"1","kodeThree":"012001","nilaiPulsa":"000000005000","noHp":"0895385381299","transactionId":"0612143023138365102 ","voucherSerialNumber":"20170612143620980249"},"reff":"20170612143620980249","loadTime":"0.0107","tipe_request":"purchase","prv":"hpay","userID":"1002003","idpel":"0895385381299","product":"PULSA","produk":"PULSA","product_detail":"THREE5000","produk_tipe":"THREE5000","traceId":16,"tagihan":5050,"total_tagihan":5050,"amount":5050,"saldo":94950,"waktu":"2017-07-17 18:43:32"}'; | ||
25 | const responseData = partner.parseResponseBody(responseBody); | ||
26 | |||
27 | describe('#parseResponseBody', function() { | ||
28 | |||
29 | it('should return correct response code', function() { | ||
30 | responseData.response_code.should.equal('0000'); | ||
31 | }) | ||
32 | |||
33 | it('should return correct voucher serial number', function() { | ||
34 | responseData.detail.voucherSerialNumber.should.equal('20170612143620980249'); | ||
35 | }) | ||
36 | }) | ||
37 | |||
38 | describe('#responseDataProcessor', function() { | ||
39 | const processed = partner.responseDataProcessor(responseData); | ||
40 | |||
41 | it('should return correct rc', function() { | ||
42 | processed.rc.should.equal('00'); | ||
43 | }) | ||
44 | |||
45 | it('should return correct sn', function() { | ||
46 | processed.sn.should.equal('20170612143620980249'); | ||
47 | }) | ||
48 | |||
49 | it('should return correct amount', function() { | ||
50 | processed.amount.should.equal(5050); | ||
51 | }) | ||
52 | |||
53 | it('should return correct balance', function() { | ||
54 | processed.balance.should.equal(94950); | ||
55 | }) | ||
56 | }) | ||
57 | }) | ||
58 |