telebirr-receipt 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of telebirr-receipt might be problematic. Click here for more details.

Files changed (4) hide show
  1. package/README.md +9 -52
  2. package/index.js +39 -39
  3. package/package.json +9 -7
  4. package/LICENSE +0 -14
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  `telebirr-receipt` Is a small nodejs package to parse and verify
6
6
  telebirr transaction receipt. Any telebirr transaction
7
- receipt can be found through the url
7
+ receipt can be found through url
8
8
  `https://transactioninfo.ethiotelecom.et/receipt/{receipt_no}`
9
9
  by replacing `{receipt_no}` with receipt number or id which is sent from telebirr
10
10
  transaction SMS or the full receipt url is also sent via SMS.
@@ -22,13 +22,13 @@ npm install telebirr-receipt
22
22
  ```javascript
23
23
  const {receipt, utils: {loadReceipt, parseFromHTML}} = require('telebirr-receipt')
24
24
 
25
- // use it with caution / security warning
25
+ // for debugging only security warning
26
26
  process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
27
27
 
28
- // TODO: replace `receiptNo`
29
- loadReceipt({receiptNo: 'ADQ...'}).then(htmlAsString => {
30
- const {verify, verifyOnly, verifyAll, equals} = receipt(parseFromHTML(htmlAsString), {to: 'Someone'})
31
- verify((parsedFields, preDefinedFields) => {
28
+ // TODO: replace `receipt_no`
29
+ loadReceipt({receipt_no: "ADQ..."}).then(htmlAsString => {
30
+ const {verify, verifyOnly, verifyAll, equals} = receipt(parseFromHTML(htmlAsString), {to: "Someone"})
31
+ verify((parsed_fields, pre_defined_fields) => {
32
32
  // do anything with receipt
33
33
  })
34
34
  }).catch(reason => {
@@ -41,7 +41,7 @@ loadReceipt({receiptNo: 'ADQ...'}).then(htmlAsString => {
41
41
  #### Using custom receipt parser with custom string source
42
42
 
43
43
  ```javascript
44
- const {receipt} = require('telebirr-receipt');
44
+ const {receipt} = require("telebirr-receipt");
45
45
 
46
46
  function parse() {
47
47
  const source = 'payer_name=Abebe;amount=5000;to=Kebede'
@@ -58,50 +58,7 @@ function parse() {
58
58
  const {verifyAll, verify, equals} = receipt(parse(), {to: 'Kebede', amount: '5000'})
59
59
 
60
60
  // match expected receiver's name with the name found on receipt
61
- console.log(verify((parsedInfo, myInfo) => equals(parsedInfo?.to, myInfo?.to))) // match any field
61
+ console.log(verify((parsed_info, my_info) => equals(parsed_info?.to, my_info?.to)))
62
62
 
63
63
  console.log(verifyAll(['payer_name'])) // match every field but `payer_name`
64
- ```
65
-
66
- ### Use case
67
-
68
- The main use case of this project is to overcome payment api unavailability and requirements in Ethiopia. Based on this
69
- project you can get paid and verify your payment transactions.
70
-
71
- 1. Your customers may fill their personal or payment information
72
- 2. Your customers pay you via telebirr
73
- 3. You let your customers know how to verify their payment by entering their transaction number or link which is
74
- received from 127 / telebirr SMS.
75
- 4. So you can verify that who send the money to you.
76
-
77
- ### IMPORTANT
78
-
79
- The telebirr receipt website may be modified or changed at anytime, so it is important to test HTML parser every time
80
- before letting users pay and before verifying.
81
- You could test the parser simply by hard coding a sample transaction information manually and matching it with parsed
82
- from url.
83
-
84
- ### Known issues
85
-
86
- 1. `loadReceipt` fails with error: `unable to verify the first certificate`, code: `UNABLE_TO_VERIFY_LEAF_SIGNATURE`.
87
- Disabling certificate verification could fix the problem, but it is not recommended.
88
- Let me know if there is good solution to this problem.
89
- 2. `parseFromHTML` may return different receipt field values or even empty and omit field values because of
90
- the variability of the receipt website based on transaction such as transferring to bank and direct telebirr
91
- transferring have different receipt format and soon. Thus, you should take a look at the return data of the
92
- function `parseFromHTML` for different transaction destinations.
93
-
94
- ### DISCLAIMER
95
-
96
- The open-source code provided is offered "as is" without warranty of any kind, either express or implied, including, but
97
- not limited to, the implied warranties of merchantability and fitness for a particular purpose. The author of this code
98
- shall not be liable for any damages arising from the use of this code, including, but not limited to, direct, indirect,
99
- incidental, special, or consequential damages.
100
-
101
- Users of this open-source code are solely responsible for testing and verifying the code's suitability for their
102
- specific purposes. The author of this code makes no representations or warranties about the code's accuracy,
103
- reliability, completeness, or timeliness. The user assumes all risk associated with the use of this code and agrees to
104
- indemnify and hold harmless the author of this code from any and all claims, damages, or liabilities arising from the
105
- use of this code.
106
-
107
- By using this open-source code, the user acknowledges and agrees to these terms and conditions.
64
+ ```
package/index.js CHANGED
@@ -1,45 +1,45 @@
1
1
  /**
2
2
  * Initialize receipt
3
- * @param {Object} parsedFields Parsed receipt information
4
- * @param {Object} preDefinedFields Expected receipt information, used for verification
3
+ * @param {Object} parsed_fields Parsed receipt information
4
+ * @param {Object} pre_defined_fields Expected receipt information, used for verification
5
5
  * @return {Object}
6
6
  */
7
- function receipt(parsedFields = {}, preDefinedFields = {}) {
7
+ function receipt(parsed_fields = {}, pre_defined_fields = {}) {
8
8
  /**
9
9
  * Compare two strings or numbers
10
- * @param {string|number} a
11
- * @param {string|number} b
12
- * @return {boolean} `true` if a and b are both string or number and equal otherwise `false`
10
+ * @param {string} a
11
+ * @param {string} b
12
+ * @return {Boolean} `true` if a and b are both string or number and equal otherwise `false`
13
13
  */
14
14
  function equals(a, b) {
15
15
  // considering `undefined === undefined`
16
- return (typeof a === 'string' && typeof b === 'string' || typeof a === 'number' && typeof b === 'number') && a === b
16
+ return (((typeof a == 'string' && typeof b == 'string') || (typeof a == 'number' && typeof b == 'number')) && a === b)
17
17
  }
18
18
 
19
19
  /**
20
20
  * Verify any parsed field with its corresponding predefined field
21
- * @param {function(Object, Object) : boolean} callback Callback function with two parameters `parsedFields` and `preDefinedFields` respectively
22
- * @return {boolean} the return value from the callback function, should return `boolean`
21
+ * @param {Function} callback Callback function with two parameters `parsed_fields` and `pre_defined_fields` respectively
22
+ * @return {Boolean} the return value from the callback function, should return `boolean`
23
23
  */
24
24
  function verify(callback) {
25
- return callback(parsedFields, preDefinedFields)
25
+ return callback(parsed_fields, pre_defined_fields)
26
26
  }
27
27
 
28
28
  /**
29
29
  * Match or verify all parsed fields with their corresponding predefined fields
30
- * `parsedFields` and `preDefinedFields` must have the same name
30
+ * `parsed_fields` and `pre_defined_fields` must have the same name
31
31
  * @param {[string]} doNotCompare List of field names to ignore from checking
32
- * @return {boolean} `true` if all fields match otherwise `false`, if `parsedFields` are empty returns `false`
32
+ * @return {Boolean} `true` if all fields match otherwise `false`, if `parsed_fields` are empty returns `false`
33
33
  */
34
34
  function verifyAll(doNotCompare = []) {
35
- if (Object.keys(parsedFields).length <= 0) {
35
+ if (Object.keys(parsed_fields).length <= 0) {
36
36
  return false
37
37
  }
38
38
 
39
- for (const key in parsedFields) {
40
- if (Object.hasOwnProperty.call(parsedFields, key)) {
39
+ for (const key in parsed_fields) {
40
+ if (Object.hasOwnProperty.call(parsed_fields, key)) {
41
41
  if (!doNotCompare.includes(key)) {
42
- if (!equals(parsedFields[key], preDefinedFields[key])) {
42
+ if (!equals(parsed_fields[key], pre_defined_fields[key])) {
43
43
  return false
44
44
  }
45
45
  }
@@ -51,17 +51,17 @@ function receipt(parsedFields = {}, preDefinedFields = {}) {
51
51
 
52
52
  /**
53
53
  * Match or verify specified list of fields with their corresponding predefined fields
54
- * `parsedFields` and `preDefinedFields` must have the same name
55
- * @param {[string]} fieldNames List of field names to check
56
- * @return {boolean} `true` if all fields match otherwise `false`, if empty `fieldNames` passed returns `false`
54
+ * `parsed_fields` and `pre_defined_fields` must have the same name
55
+ * @param {[string]} field_names List of field names to check
56
+ * @return {Boolean} `true` if all fields match otherwise `false`, if empty `field_names` passed returns `false`
57
57
  */
58
- function verifyOnly(fieldNames = []) {
59
- if (fieldNames.length <= 0) {
58
+ function verifyOnly(field_names = []) {
59
+ if (field_names.length <= 0) {
60
60
  return false
61
61
  }
62
62
 
63
- fieldNames.forEach(key => {
64
- if (!equals(fieldNames[key], preDefinedFields[key])) {
63
+ field_names.forEach(key => {
64
+ if (!equals(field_names[key], pre_defined_fields[key])) {
65
65
  return false
66
66
  }
67
67
  })
@@ -75,7 +75,7 @@ function receipt(parsedFields = {}, preDefinedFields = {}) {
75
75
  /**
76
76
  * Parse fields from HTML string
77
77
  * @param {string} str HTML string
78
- * @return {{payer_name:string?, payer_phone:string?, payer_acc_type:string?, credited_party_name:string?, credited_party_acc_no:string?, transaction_status:string?, bank_acc_no:string?, receiptNo:string?, date:string?, settled_amount:number?, discount_amount:number?, vat_amount:number?, total_amount:number?, amount_in_word:string?, payment_mode:string?, payment_reason:string?, payment_channel:string?}} `parsedFields` as an object with field names and values
78
+ * @return {Object} `parsed_fields` as an object with field names `payer_name`, `payer_phone`, `payer_acc_type`, `credited_party_name`, `credited_party_acc_no`, `transaction_status`, `bank_acc_no`, `receipt_no`, `date`, `settled_amount`, `discount_amount`, `vat_amount`, `total_amount`, `amount_in_word`, `payment_mode`, `payment_reason`, `payment_channel`
79
79
  */
80
80
  function parseFromHTML(str) {
81
81
  const htmlparser = require('htmlparser2')
@@ -90,21 +90,21 @@ function parseFromHTML(str) {
90
90
  return ''
91
91
  }
92
92
 
93
- const names = ['የከፋይስም/payername', 'የከፋይቴሌብርቁ./payertelebirrno.', 'የከፋይአካውንትአይነት/payeraccounttype', 'የገንዘብተቀባይስም/creditedpartyname', 'የገንዘብተቀባይቴሌብርቁ./creditedpartyaccountno', 'የክፍያውሁኔታ/transactionstatus', 'የባንክአካውንትቁጥር/bankaccountnumber', 'የክፍያቁጥር/receiptno.', 'የክፍያቀን/paymentdate', 'የተከፈለውመጠን/settledamount', 'ቅናሽ/discountamount', '15%ቫት/vat', 'ጠቅላላየተክፈለ/totalamountpaid', 'የገንዘቡልክበፊደል/totalamountinword', 'የክፍያዘዴ/paymentmode', 'የክፍያምክንያት/paymentreason', 'የክፍያመንገድ/paymentchannel']
94
- const keys = ['payer_name', 'payer_phone', 'payer_acc_type', 'credited_party_name', 'credited_party_acc_no', 'transaction_status', 'bank_acc_no', 'receiptNo', 'date', 'settled_amount', 'discount_amount', 'vat_amount', 'total_amount', 'amount_in_word', 'payment_mode', 'payment_reason', 'payment_channel']
93
+ const names = ["የከፋይስም/payername", "የከፋይቴሌብርቁ./payertelebirrno.", "የከፋይአካውንትአይነት/payeraccounttype", "የገንዘብተቀባይስም/creditedpartyname", "የገንዘብተቀባይቴሌብርቁ./creditedpartyaccountno", "የክፍያውሁኔታ/transactionstatus", "የባንክአካውንትቁጥር/bankaccountnumber", "የክፍያቁጥር/receiptno.", "የክፍያቀን/paymentdate", "የተከፈለውመጠን/settledamount", "ቅናሽ/discountamount", "15%ቫት/vat", "ጠቅላላየተክፈለ/totalamountpaid", "የገንዘቡልክበፊደል/totalamountinword", "የክፍያዘዴ/paymentmode", "የክፍያምክንያት/paymentreason", "የክፍያመንገድ/paymentchannel"]
94
+ const keys = ['payer_name', 'payer_phone', 'payer_acc_type', 'credited_party_name', "credited_party_acc_no", "transaction_status", "bank_acc_no", "receipt_no", "date", "settled_amount", "discount_amount", "vat_amount", "total_amount", "amount_in_word", "payment_mode", "payment_reason", "payment_channel"]
95
95
 
96
96
  td.forEach((value, index) => {
97
97
  let s = htmlparser.DomUtils.textContent(value).replace(/[\n\r\s\t]/ig, '').trim().toLowerCase();
98
98
 
99
99
  const indexOf = names.indexOf(s)
100
100
  if (indexOf >= 0) {
101
- let nextValue = getNextValue(index + (indexOf > 6 && indexOf <= 9 ? 2 : 0));
101
+ let nextValue = getNextValue(index + ((indexOf > 6 && indexOf <= 9) ? 2 : 0));
102
102
  let key = keys[indexOf];
103
103
 
104
- if (key === 'bank_acc_no') {
104
+ if (key === "bank_acc_no") {
105
105
  fields['to'] = nextValue.replace(/^[0-9]+/g, '').trim()
106
106
  fields[key] = nextValue.replace(/[A-Z]/ig, '').trim()
107
- } else if (key.endsWith('amount')) {
107
+ } else if (key.endsWith("amount")) {
108
108
  fields[key] = parseFloat(nextValue.replace(/birr/ig, '').trim())
109
109
  } else {
110
110
  fields[key] = nextValue
@@ -117,32 +117,32 @@ function parseFromHTML(str) {
117
117
 
118
118
  /**
119
119
  * Load receipt HTML string from receipt number or any url
120
- * @param {string} [receiptNo] Receipt number received from telebirr SMS, high priority
121
- * @param {string} [fullUrl] Or full receipt url, less prior
120
+ * @param {string?} receipt_no Receipt number received from telebirr SMS, high priority
121
+ * @param {string?} full_url Or full receipt url, less prior
122
122
  * @return {Promise<String>} HTML string
123
123
  */
124
- function loadReceipt({receiptNo, fullUrl}) {
124
+ function loadReceipt({receipt_no, full_url}) {
125
125
  return new Promise((resolve, reject) => {
126
- const url = receiptNo ? `https://transactioninfo.ethiotelecom.et/receipt/${receiptNo}` : fullUrl
126
+ const url = receipt_no ? `https://transactioninfo.ethiotelecom.et/receipt/${receipt_no}` : full_url
127
127
 
128
- const https = require('https');
128
+ const https = require("https");
129
129
  const options = new URL(url)
130
130
 
131
- let data = ''
131
+ let data = ""
132
132
  const req = https.request(options, (res) => {
133
- res.on('data', (chunk) => {
133
+ res.on("data", (chunk) => {
134
134
  data += chunk
135
135
  })
136
136
 
137
- res.on('error', (error) => {
137
+ res.on("error", (error) => {
138
138
  reject(error)
139
139
  })
140
140
 
141
- res.on('end', () => {
141
+ res.on("end", () => {
142
142
  resolve(data)
143
143
  })
144
144
  })
145
- req.on('error', (error) => {
145
+ req.on("error", (error) => {
146
146
  reject(error)
147
147
  })
148
148
  req.end()
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "telebirr-receipt",
3
3
  "description": "Telebirr transaction receipt parser and verifier",
4
- "version": "0.0.1",
5
- "author": "Yonathan Tesfaye <yt040448@gmail.com>",
4
+ "version": "1.0.0",
6
5
  "main": "index.js",
7
6
  "scripts": {
8
7
  "start": "node index.js",
@@ -16,16 +15,19 @@
16
15
  "parse",
17
16
  "transaction"
18
17
  ],
19
- "license": "Apache-2.0",
18
+ "author": {
19
+ "name": "Yonathan Tesfaye",
20
+ "email": "yt040448@gmail.com"
21
+ },
22
+ "license": "Apache",
20
23
  "repository": {
21
- "type": "git",
22
- "url": "git://github.com/TheRealYT/telebirr-receipt.git"
24
+ "url": "https://github.com/TheRealYT/telebirr-receipt"
23
25
  },
24
26
  "files": [
25
27
  "README.md",
26
- "LICENSE",
28
+ "index.js",
27
29
  "package.json",
28
- "index.js"
30
+ "package-lock.json"
29
31
  ],
30
32
  "dependencies": {
31
33
  "htmlparser2": "^9.0.0"
package/LICENSE DELETED
@@ -1,14 +0,0 @@
1
- Copyright 2023 Yonathan Tesfaye
2
- Email: yt040448@gmail.com
3
-
4
- Licensed under the Apache License, Version 2.0 (the "License");
5
- you may not use this file except in compliance with the License.
6
- You may obtain a copy of the License at
7
-
8
- http://www.apache.org/licenses/LICENSE-2.0
9
-
10
- Unless required by applicable law or agreed to in writing, software
11
- distributed under the License is distributed on an "AS IS" BASIS,
12
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- See the License for the specific language governing permissions and
14
- limitations under the License.