├── .gitignore ├── package.json ├── .github └── workflows │ └── npm-publish.yml ├── README.md └── telebirr.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telebirrjs", 3 | "version": "1.2.0", 4 | "description": "", 5 | "keywords": [ 6 | "telebirr", 7 | "payment", 8 | "fintech", 9 | "ethiopia" 10 | ], 11 | "main": "telebirr.js", 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/vernu/telebirrjs.git" 18 | }, 19 | "author": "@vernu", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/vernu/telebirrjs/issues" 23 | }, 24 | "homepage": "https://github.com/vernu/telebirrjs#readme", 25 | "dependencies": { 26 | "axios": "^0.27.2", 27 | "node-rsa": "^1.1.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | - run: npm ci 19 | # - run: npm test 20 | 21 | publish-npm: 22 | needs: build 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: actions/setup-node@v3 27 | with: 28 | node-version: 16 29 | registry-url: https://registry.npmjs.org/ 30 | - run: npm ci 31 | - run: npm publish 32 | env: 33 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # telebirrjs 2 | 3 | hustle free telebirr integration package for node 4 | 5 | ## Usage 6 | 7 | install the package `npm i telebirrjs` 8 | 9 | ```javascript 10 | const Telebirr = require('telebirrjs') 11 | 12 | const telebirr = new Telebirr({ 13 | appId: 'YOUR TELEBIRR APP ID', 14 | appKey: 'YOUR TELEBIRR APP KEY', 15 | shortCode: 'TELEBIRR SHORT CODE', 16 | publicKey: 'YOUR TELEBIRR PUBLIC KEY', 17 | }) 18 | const { success, response } = await telebirr.makePayment({ 19 | paymentMethod: 'web | app', 20 | nonce: 'a unique random string ( should be unique for each request )', 21 | notifyUrl: 'callback url for payment confirmation', 22 | totalAmount: 4.5, // amount to charge 23 | outTradeNo: 'unique identifier (order no)', 24 | receiveName: 'company name', 25 | returnApp: 'com.example.app', // your application package name 26 | returnUrl: 'https://yourwebsite.com', // redirect url after payment completion' 27 | subject: 'payment for', 28 | timeoutExpress: '120', // valid for 2 hours 29 | }) 30 | ``` 31 | 32 | ## Handling callback notifications 33 | 34 | - once user completes the payment, you'll receive an encrypted plaintext on the `notifyUrl` you provided. 35 | 36 | - Make sure the endpoint accepts `plaintext` request body 37 | 38 | - you need to decrypt the text to get the transaction details 39 | 40 | ```javascript 41 | const { 42 | msisdn, // the phone number from which the payment was done 43 | outTradeNo, // unique identifier provided when creating the payment 44 | totalAmount, 45 | tradeDate, 46 | tradeNo, 47 | tradeStatus, 48 | transactionNo, 49 | } = telebirr.getDecryptedCallbackNotification(encryptedTextFromTelebirr) 50 | ``` 51 | -------------------------------------------------------------------------------- /telebirr.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | const NodeRSA = require('node-rsa') 3 | const axios = require('axios') 4 | 5 | const TELEBIRR_H5_URL = 6 | 'https://app.ethiomobilemoney.et:2121/ammapi/payment/service-openup/toTradeWebPay' 7 | const TELEBIRR_IN_APP_URL = 8 | 'https://app.ethiomobilemoney.et:2121/ammapi/payment/service-openup/toTradeMobilePay' 9 | 10 | class Telebirr { 11 | constructor({ appId, appKey, shortCode, publicKey }) { 12 | this.appId = appId 13 | this.appKey = appKey 14 | this.shortCode = shortCode 15 | this.publicKey = publicKey 16 | } 17 | 18 | async makePayment({ 19 | paymentMethod = 'web', 20 | nonce, 21 | notifyUrl, 22 | totalAmount, 23 | outTradeNo, 24 | receiveName, 25 | returnApp='com.example.app', 26 | returnUrl, 27 | subject, 28 | timeoutExpress = `${24 * 60}`, // 1 day 29 | }) { 30 | const params = { 31 | appId: this.appId, 32 | appKey: this.appKey, 33 | nonce, 34 | notifyUrl, 35 | outTradeNo, 36 | receiveName, 37 | returnApp, 38 | returnUrl, 39 | shortCode: this.shortCode, 40 | subject, 41 | timeoutExpress, 42 | timestamp: new Date().getTime(), 43 | totalAmount, 44 | } 45 | 46 | const url = paymentMethod == 'app' ? TELEBIRR_IN_APP_URL : TELEBIRR_H5_URL 47 | 48 | const payload = { 49 | appid: this.appId, 50 | sign: this.signData(params), 51 | ussd: this.encrypt(params), 52 | } 53 | try { 54 | const res = await axios.post(url, payload) 55 | 56 | return { success: res.data.code == 200, response: res.data } 57 | } catch (e) { 58 | console.log(e) 59 | return { success: false, error: e } 60 | } 61 | } 62 | 63 | encrypt(payload) { 64 | const rsaKey = new NodeRSA( 65 | `-----BEGIN PUBLIC KEY-----\n${this.publicKey}\n-----END PUBLIC KEY-----`, 66 | 'public', 67 | { 68 | encryptionScheme: 'pkcs1', 69 | } 70 | ) 71 | const dataToEncrypt = Buffer.from(JSON.stringify(payload)) 72 | return rsaKey.encrypt(dataToEncrypt, 'base64', 'utf8') 73 | } 74 | 75 | signData(fields) { 76 | const encodedFields = Object.keys(fields) 77 | .sort() 78 | .map((key) => `${key}=${fields[key]}`) 79 | .join('&') 80 | 81 | return crypto.createHash('sha256').update(encodedFields).digest('hex') 82 | } 83 | 84 | decryptPublic(dataToDecrypt) { 85 | const rsaKey = new NodeRSA( 86 | `-----BEGIN PUBLIC KEY-----\n${this.publicKey}\n-----END PUBLIC KEY-----`, 87 | 'public', 88 | { 89 | encryptionScheme: 'pkcs1', 90 | } 91 | ) 92 | return rsaKey.decryptPublic(dataToDecrypt, 'utf8') 93 | } 94 | 95 | getDecryptedCallbackNotification(encryptedText) { 96 | const decryptedText = this.decryptPublic(encryptedText) 97 | return JSON.parse(decryptedText) 98 | } 99 | } 100 | 101 | module.exports = Telebirr 102 | --------------------------------------------------------------------------------