├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── index.js ├── lib ├── helper.js └── parser.js ├── package.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "prettier"], 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "prettier/prettier": ["error"] 6 | } 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true 4 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Achmad Apriady 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nodejs-bca-scraper 2 | 3 | Plugin untuk membantu anda mendapatkan informasi saldo terakhir rekening BCA anda serta mutasi rekening BCA anda pada hari itu melalui KlikBCA. 4 | 5 | ## Cara Install 6 | 7 | ```bash 8 | npm install --save nodejs-bca-scraper 9 | ``` 10 | 11 | atau 12 | 13 | ```bash 14 | yarn add nodejs-bca-scraper 15 | ``` 16 | 17 | ## Penggunaan 18 | 19 | ```javascript 20 | const bca = require('nodejs-bca-scraper'); 21 | ``` 22 | 23 | ### Cek Saldo Terakhir 24 | 25 | ```javascript 26 | bca 27 | .getBalance(USERNAME, PASSWORD) 28 | .then(res => { 29 | console.log('saldo ', res); 30 | }) 31 | .catch(err => { 32 | console.log('error ', err); 33 | }); 34 | ``` 35 | 36 | ### Cek Settlement Pada Hari Itu 37 | 38 | ```javascript 39 | bca 40 | .getSettlement(USERNAME, PASSWORD) 41 | .then(res => { 42 | console.log('settlement ', res); 43 | }) 44 | .catch(err => { 45 | console.log('error ', err); 46 | }); 47 | ``` 48 | 49 | # License 50 | 51 | MIT 52 | 53 | # Author 54 | 55 | [Achmad Apriady](mailto:achmad.apriady@gmail.com) 56 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const parser = require('./lib/parser'); 2 | 3 | module.exports = { 4 | getBalance: async (username, password) => { 5 | try { 6 | const IP = await parser.getIP(); 7 | await parser.login(username, password, IP); 8 | await parser.openSettlementMenu(); 9 | const balance = await parser.balance(); 10 | await parser.logout(); 11 | return balance; 12 | } catch (err) { 13 | await parser.logout(); 14 | throw err; 15 | } 16 | }, 17 | 18 | getSettlement: async (username, password) => { 19 | try { 20 | const IP = await parser.getIP(); 21 | await parser.login(username, password, IP); 22 | await parser.openSettlementMenu(); 23 | const settlement = await parser.settlement(); 24 | await parser.logout(); 25 | return settlement; 26 | } catch (err) { 27 | await parser.logout(); 28 | throw err; 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /lib/helper.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stringBetween: (input, start, end) => { 3 | return input.match(new RegExp(`${start}([\\S\\s]*?)${end}`))[1]; 4 | }, 5 | tdValue: input => { 6 | return input.match(/]*?>(.*?)<\/td>/g); 7 | }, 8 | removeHtml: input => { 9 | return input.replace(/<[^>]*>?/gm, ''); 10 | }, 11 | toNumber: input => { 12 | return Number(input.replace(/,/g, '')); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /lib/parser.js: -------------------------------------------------------------------------------- 1 | const request = require('request-promise-native'); 2 | const moment = require('moment'); 3 | const { stringBetween, tdValue, removeHtml, toNumber } = require('./helper'); 4 | 5 | const j = request.jar(); 6 | const rp = request.defaults({ 7 | headers: { 8 | 'User-Agent': 9 | 'Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1' 10 | }, 11 | jar: j 12 | }); 13 | 14 | module.exports = { 15 | getIP: async () => { 16 | const ipify = await request({ 17 | uri: 'https://api.ipify.org/?format=json', 18 | json: true 19 | }); 20 | return ipify.ip; 21 | }, 22 | 23 | login: (username, password, ip) => { 24 | const options = { 25 | method: 'POST', 26 | uri: 'https://m.klikbca.com/authentication.do', 27 | form: { 28 | 'value(user_id)': username, 29 | 'value(pswd)': password, 30 | 'value(Submit)': 'LOGIN', 31 | 'value(actions)': 'login', 32 | 'value(user_ip)': ip, 33 | user_ip: ip, 34 | 'value(mobile)': true, 35 | mobile: true 36 | }, 37 | headers: { 38 | Referer: 'https://m.klikbca.com/login.jsp' 39 | } 40 | }; 41 | return rp(options) 42 | .then(result => { 43 | const success = result.includes('MENU UTAMA'); 44 | if (!success) { 45 | let err = stringBetween(result, "var err='", "';"); 46 | err = err || ''; 47 | throw Error(err); 48 | } 49 | return true; 50 | }) 51 | .catch(err => { 52 | throw err.message; 53 | }); 54 | }, 55 | 56 | openSettlementMenu: () => { 57 | const options = { 58 | method: 'POST', 59 | uri: 'https://m.klikbca.com/accountstmt.do?value(actions)=menu', 60 | headers: { 61 | Referer: 'https://m.klikbca.com/authentication.do' 62 | } 63 | }; 64 | return rp(options); 65 | }, 66 | 67 | balance: () => { 68 | const options = { 69 | method: 'POST', 70 | uri: 'https://m.klikbca.com/balanceinquiry.do', 71 | headers: { 72 | Referer: 'https://m.klikbca.com/accountstmt.do?value(actions)=menu' 73 | } 74 | }; 75 | return rp(options) 76 | .then(result => { 77 | return { 78 | rekening: stringBetween(result, "", ''), 79 | saldo: toNumber( 80 | stringBetween(result, "", '') 81 | ) 82 | }; 83 | }) 84 | .catch(err => { 85 | throw err.message; 86 | }); 87 | }, 88 | 89 | settlement: async () => { 90 | const options = { 91 | method: 'POST', 92 | uri: 'https://m.klikbca.com/accountstmt.do?value(actions)=acct_stmt', 93 | headers: { 94 | Referer: 'https://m.klikbca.com/accountstmt.do?value(actions)=menu' 95 | } 96 | }; 97 | try { 98 | const now = moment(); 99 | const date = now.format('DD'); 100 | const month = now.format('MM'); 101 | const year = now.format('YYYY'); 102 | 103 | await rp(options); 104 | options.uri = 'https://m.klikbca.com/accountstmt.do?value(actions)=acctstmtview'; 105 | options.headers.Referer = 'https://m.klikbca.com/accountstmt.do?value(actions)=acct_stmt'; 106 | options.form = { 107 | r1: 1, 108 | 'value(D1)': 0, 109 | 'value(startDt)': date, 110 | 'value(startMt)': month, 111 | 'value(startYr)': year, 112 | 'value(endDt)': date, 113 | 'value(endMt)': month, 114 | 'value(endYr)': year 115 | }; 116 | const result = await rp(options); 117 | const cleanStmt = []; 118 | if (!result.includes('TIDAK ADA TRANSAKSI')) { 119 | let stmt = stringBetween(result, 'KETERANGAN', '