telebirr-receipt 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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.