├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── changelog.md ├── dist ├── crypto-bot-api.js ├── crypto-bot-api.js.map ├── crypto-bot-api.min.js └── crypto-bot-api.min.js.map ├── eslint.config.mjs ├── examples ├── create-invoice.js ├── create-invoice.ts ├── exchange-rate-ticker.js ├── exchange-rate-ticker.ts ├── get-app-information.js ├── get-app-information.ts ├── get-invoices-paginate.js ├── get-invoices-paginate.ts ├── get-invoices.js ├── get-invoices.ts ├── webhooks-express-receiving.js ├── webhooks-express-receiving.ts ├── webhooks-receiving.js └── webhooks-receiving.ts ├── lib ├── classes │ ├── Client.d.ts │ ├── Client.js │ ├── ClientEmitter.d.ts │ ├── ClientEmitter.js │ ├── Store.d.ts │ ├── Store.js │ ├── Transport.d.ts │ └── Transport.js ├── helpers │ ├── casts.d.ts │ ├── casts.js │ ├── utils.d.ts │ └── utils.js ├── index.d.ts ├── index.js ├── request │ ├── http.d.ts │ ├── http.js │ ├── xhr.d.ts │ └── xhr.js ├── types.d.ts └── types.js ├── license ├── package-lock.json ├── package.json ├── readme.md ├── rollup.config.mjs ├── src ├── classes │ ├── Client.ts │ ├── ClientEmitter.ts │ ├── Store.ts │ └── Transport.ts ├── helpers │ ├── casts.ts │ └── utils.ts ├── index.ts ├── request │ ├── http.ts │ └── xhr.ts └── types.ts └── tsconfig.json /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy documentation to Pages 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | pages: write 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | - name: Setup Node 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: 20 21 | - name: Install 22 | run: npm ci 23 | - name: Build 24 | run: npm run build-docs 25 | - name: Push 26 | uses: peaceiris/actions-gh-pages@v4 27 | with: 28 | github_token: ${{ secrets.GITHUB_TOKEN }} 29 | publish_dir: docs 30 | force_orphan: true 31 | disable_nojekyll: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docs 2 | node_modules -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.3.5 - 2025-03-24 4 | 5 | - Add new support `MEMHASH` currency code to types 6 | - Fix `paid` event arguments optional status 7 | - Fix `http` create server field required status 8 | - Update dependencies, fix build config 9 | - Add `TypeScript` examples, fix examples errors 10 | 11 | ## 0.3.4 - 2025-01-30 12 | 13 | - Add new support currencies codes to types 14 | 15 | Thanks: 16 | 17 | - [Ryuko](https://github.com/helloryuko) 18 | 19 | ## 0.3.3 - 2024-09-18 20 | 21 | - Fix default export type 22 | 23 | ## 0.3.2 - 2024-09-17 24 | 25 | - Fix export types 26 | - Readme fixes 27 | 28 | ## 0.3.1 - 2024-09-17 29 | 30 | - Added ability to create HTTP server, not only HTTPS 31 | - Change empty request body error response code to `401` 32 | - Readme fixes 33 | - Update dependencies 34 | 35 | Thanks: 36 | 37 | - [Nikita](https://github.com/asymptotee) 38 | 39 | ## 0.3.0 - 2024-09-16 40 | 41 | - Add `getStats`, `createCheck`, `getChecks`, `getChecksPaginate`, `transfer`, `getTransfers`, `getTransfersPaginate`, `deleteInvoice` and `deleteCheck` methods 42 | - Change methods `getInvoices` and `getInvoicesPaginate` to returns array of `Invoice` 43 | - Remove `currency` field from `GetInvoicesOptions` and `GetInvoicesPaginateOptions` types and add `asset` and `fiat` fields 44 | - Update `Invoice` type to actual API version 45 | - Update `createInvoice` method to actual API version, change related options types 46 | - Change return type of `getExchangeRate` method and field `rate` of `ExchangeRate` type which returned by `getExchangeRates` method to string 47 | - Change `Invoice` type `amount` field type to string 48 | - Change `getInvoices` options `ids` field type to `number[]` 49 | - Fix `getInvoices` backend method `invoice_ids` parameter type 50 | - Remove calculating and returning exchange rate for reverse pair of `source` and `target` by `getExchangeRate` method 51 | - Add `isValid` field to `ExchangeRate` type 52 | - Change `getExchangeRate` method to return '0' value for non-valid exchange rate 53 | - Change `Balances` type to store available and on hold balances in new `Balance` type 54 | - Create `BalancesType` type to store available or on hold balances 55 | - Remove `isReturnInNanos` and `isForce` parameters from `getBalances` and `getBalance` methods 56 | - Add `getBalancesAvailable`, `getBalancesOnhold`, `getBalanceAvailable` and `getBalanceOnhold` to get balances by type 57 | - Create `CurrencyCode` type with known variants 58 | - Add `code` field to `Currency` type 59 | - Add `unknown` variant for `CurrencyType` 60 | - Replace `CurrencyType` by enum and store it in `Client` class 61 | - Rename `CurrencyType` to `DetailedCurrencyType` with more detailed variants 62 | - Use `CurrencyType` type to main types variants and store it in `Client` class 63 | - Replace `InvoiceStatus` by enum and store it in `Client` class 64 | - Fix library connection in examples 65 | - Fix links in `readme.md` 66 | - Fix `Client` class `setPageSize` method documentation parameter name 67 | - Fix `ClientEmitter.constructor` documentation inheritance 68 | - Update dependencies 69 | 70 | ## 0.2.1 - 2024-04-23 71 | 72 | - Fix `getInvoices` identifiers parameter name 73 | 74 | Thanks: 75 | 76 | - [Deyvan](https://github.com/Deyvan) 77 | 78 | ## 0.2.0 - 2021-12-09 79 | 80 | - Using mainnet endpoint for default 81 | 82 | ## 0.1.1 - 2021-12-05 83 | 84 | - Webhooks request data and check signature changes 85 | 86 | ## 0.1.0 - 2021-12-04 87 | 88 | - Change backend API authorization process 89 | - Remove methods, structures and fields related with deleted from backend API methods 90 | - Add webhooks receiving in Node.js with Express.js like middleware support 91 | - Increase Client.createInvoice `payload` field max size 92 | - Add support any type in createInvoice payload and correct parsing in getInvoices response by JSON 93 | - Add direct backend API call method 94 | 95 | ## 0.0.2 - 2021-11-30 96 | 97 | - Increase package version to publish on prereserved npm package name 98 | 99 | ## 0.0.1 - 2021-11-29 100 | 101 | - Initial release -------------------------------------------------------------------------------- /dist/crypto-bot-api.min.js: -------------------------------------------------------------------------------- 1 | var CryptoBotAPI=function(){"use strict";var t,e,n,r,o,i=function(t,e){return i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},i(t,e)};"function"==typeof SuppressedError&&SuppressedError,function(t){t.Crypto="crypto",t.Fiat="fiat",t.Unknown="unknown"}(t||(t={})),function(t){t.Blockchain="blockchain",t.Stablecoin="stablecoin",t.Fiat="fiat",t.Unknown="unknown"}(e||(e={})),function(t){t.Active="active",t.Paid="paid",t.Expired="expired",t.Unknown="unknown"}(n||(n={})),function(t){t.Active="active",t.Activated="activated",t.Unknown="unknown"}(r||(r={})),function(t){t.Completed="completed",t.Unknown="unknown"}(o||(o={}));var a=function(t){return Array.isArray(t)?t.reduce((function(t,n){if(n.code){var r=n.code.toString(),o=e.Unknown;n.is_blockchain&&(o=e.Blockchain),n.is_fiat&&(o=e.Fiat),n.is_stablecoin&&(o=e.Stablecoin);var i={code:r,name:n.name||"",decimals:n.decimals||0,type:o};Object.prototype.hasOwnProperty.call(n,"url")&&(i.url=n.url),t[r]=i}return t}),{}):{}},s=function(t){return Array.isArray(t)?t.map((function(t){return{source:t.source||"",target:t.target||"",rate:t.rate,isValid:t.is_valid}})):[]},c=function(e){var r={id:e.invoice_id||0,status:e.status||n.Unknown,hash:e.hash||"",currencyType:e.currency_type||"",currency:e.asset||e.fiat||"",amount:e.amount||"0",isAllowComments:e.allow_comments||!1,isAllowAnonymous:e.allow_anonymous||!1,createdAt:new Date(e.created_at),botPayUrl:e.bot_invoice_url||"",miniAppPayUrl:e.mini_app_invoice_url||"",webAppPayUrl:e.web_app_invoice_url||""};if(r.currencyType===t.Crypto&&(r.currency=e.asset||""),r.currencyType===t.Fiat&&(r.currency=e.fiat||""),void 0!==e.hidden_message&&(r.hiddenMessage=e.hidden_message),void 0!==e.paid_anonymously&&(r.isPaidAnonymously=e.paid_anonymously),void 0!==e.expiration_date&&(r.expirationDate=new Date(e.expiration_date)),void 0!==e.paid_at&&(r.paidAt=new Date(e.paid_at)),void 0!==e.description&&(r.description=e.description),void 0!==e.paid_btn_name&&(r.paidBtnName=e.paid_btn_name),void 0!==e.paid_btn_url&&(r.paidBtnUrl=e.paid_btn_url),void 0!==e.comment&&(r.comment=e.comment),void 0!==e.paid_usd_rate&&(r.usdRate=parseFloat(e.paid_usd_rate)||0),void 0!==e.fee_asset&&(r.feeAsset=e.fee_asset||""),void 0!==e.fee_amount&&(r.fee=e.fee_amount||0),void 0!==e.accepted_assets&&(r.acceptedAssets=e.accepted_assets),void 0!==e.paid_asset&&(r.paidAsset=e.paid_asset||""),void 0!==e.paid_amount&&(r.paidAmount=parseFloat(e.paid_amount)||0),void 0!==e.paid_fiat_rate&&(r.paidFiatRate=parseFloat(e.paid_fiat_rate)||0),void 0!==e.payload){var o=void 0;try{o=JSON.parse(e.payload)}catch(t){o=e.payload}r.payload=o}return r},u=function(t){var e={id:t.check_id||0,hash:t.hash||"",asset:t.asset||"",amount:t.amount||"0",botCheckUrl:t.bot_check_url||"",status:t.status||r.Unknown,createdAt:new Date(t.created_at)};return void 0!==t.activated_at&&(e.activatedAt=new Date(t.activated_at)),void 0!==t.pin_to_user&&void 0!==t.pin_to_user.pin_by&&("id"===t.pin_to_user.pin_by&&void 0!==t.pin_to_user.user_id&&(e.pinToUserId=t.pin_to_user.user_id),"username"===t.pin_to_user.pin_by&&void 0!==t.pin_to_user.username&&(e.pinToUsername=t.pin_to_user.username)),e},d=function(t){var e={id:t.transfer_id||0,userId:t.user_id||0,asset:t.asset||"",amount:t.amount||"0",status:t.status||o.Unknown,completedAt:new Date(t.completed_at)};return void 0!==t.spend_id&&(e.spendId=t.spend_id),void 0!==t.comment&&(e.comment=t.comment),e},p=function(t){var e=[];return Array.isArray(t.items)&&(e=t.items.map(c)),e},f=function(t){var e=[];return Array.isArray(t.items)&&(e=t.items.map(u)),e},l=function(t){var e=[];return Array.isArray(t.items)&&(e=t.items.map(d)),e},_=function(t){return{id:t.app_id||0,name:t.name||"",bot:t.payment_processing_bot_username||""}},v=/^https?:\/\/((([a-z\d][a-z\d-]*[a-z\d])\.)+[a-z]{2,}|((\d{1,3}\.){3}\d{1,3}))(:\d+)?(\/[-a-z\d%_.~+]*)*$/i,h=function(t){if("number"!=typeof t||isNaN(t)||t<1)throw new Error("Identifier must be a valid positive number");return t},y=function(){function t(e,n){if(!t.KEY_CHECK_REGEXP.test(e))throw new Error("API key looks like invalid");var r,o;if("mainnet"===n)r="https://pay.crypt.bot/api";else if("testnet"===n)r="https://testnet-pay.crypt.bot/api";else{if(o=n,!v.test(o))throw new Error("Endpoint parameter not contain valid URL");r=n}this._apiKey=e,this._baseUrl="".concat(r,"/")}return t.prototype.call=function(t,e){var n,r,o="";return e&&Object.keys(e).forEach((function(t){var n=e[t];n=Array.isArray(n)?n.join(","):n.toString(),o+="&".concat(t,"=").concat(encodeURIComponent(n))})),(n=this._baseUrl+t+(o.length?"?".concat(o.substr(1)):""),r=this._apiKey,new Promise((function(t,e){var o=new XMLHttpRequest;o.open("GET",n,!0),o.setRequestHeader("Crypto-Pay-API-Token",r),o.onreadystatechange=function(){4===o.readyState&&t(o.responseText)},o.onerror=function(){e(new Error("Network Error"))},o.send()}))).then((function(t){var e;try{e=JSON.parse(t)}catch(e){throw new Error("Response parse error, raw reponse:\n".concat(t))}if(!0!==e.ok){if(!e.error)throw new Error("Api response unknown error");throw new Error("Api response error ".concat(JSON.stringify(e.error)))}return e.result}))},t.KEY_CHECK_REGEXP=/\d{1,}:[a-zA-Z0-9]{35}/,t}(),m=function(t,e,n,r){var o,i=null,a=0;return function(s){if(void 0===s&&(s=!1),i)return i;var c=Math.floor(+new Date/1e3/r);return c!==a||s?(a=c,i=t.call(e).then((function(t){return o=n(t),i=null,o}))):Promise.resolve(o)}},g=function(a){function s(){var t=null!==a&&a.apply(this,arguments)||this;return t._pageSize=100,t}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function n(){this.constructor=t}i(t,e),t.prototype=null===e?Object.create(e):(n.prototype=e.prototype,new n)}(s,a),s.prototype.getPageSize=function(){return this._pageSize},s.prototype.setPageSize=function(t){var e=+t;if(e>1e3||e<1)throw Error("Page size may be from 1 to 1000");this._pageSize=e},s.prototype.getStats=function(t){return void 0===t&&(t={}),this._transport.call("getStats",function(t){var e={};if(void 0===t.startAt&&void 0===t.endAt)return e;if(void 0===t.startAt||!(t.startAt instanceof Date))throw new Error("Field `startAt` must be a Date");if(void 0===t.endAt||!(t.endAt instanceof Date))throw new Error("Field `endAt` must be a Date");return e.start_at=t.startAt.toISOString(),e.end_at=t.endAt.toISOString(),e}(t)).then((function(t){return{volume:(e=t).volume||"0",conversion:e.conversion||"0",uniqueUsersCount:e.unique_users_count||0,createdInvoiceCount:e.created_invoice_count||0,paidInvoiceCount:e.paid_invoice_count||0,startAt:new Date(e.start_at?e.start_at:0),endAt:new Date(e.end_at?e.end_at:0)};var e}))},s.prototype.getBalances=function(){return this._transport.call("getBalance").then((function(t){return function(t){if(!Array.isArray(t))throw new Error("Input is not array: ".concat(JSON.stringify(t)));return t.reduce((function(t,e){return t[e.currency_code]={available:e.available,onhold:e.onhold},t}),{})}(t)}))},s.prototype.getBalancesAvailable=function(){return this.getBalances().then((function(t){return Object.entries(t).reduce((function(t,e){var n=e[0],r=e[1];return t[n]=r.available,t}),{})}))},s.prototype.getBalancesOnhold=function(){return this.getBalances().then((function(t){return Object.entries(t).reduce((function(t,e){var n=e[0],r=e[1];return t[n]=r.onhold,t}),{})}))},s.prototype.getBalance=function(t){return this.getBalances().then((function(e){return void 0===e[t]?{available:"0",onhold:"0"}:e[t]}))},s.prototype.getBalanceAvailable=function(t){return this.getBalances().then((function(e){return void 0===e[t]?"0":e[t].available}))},s.prototype.getBalanceOnhold=function(t){return this.getBalances().then((function(e){return void 0===e[t]?"0":e[t].onhold}))},s.prototype.getCurrency=function(t,e){return void 0===e&&(e=!1),this.getCurrencies(e).then((function(e){return void 0===e[t]?null:e[t]}))},s.prototype.getExchangeRate=function(t,e,n){return void 0===n&&(n=!1),this.getExchangeRates(n).then((function(n){return function(t,e,n){for(var r="",o=0,i=n.length;o1024)throw new Error("Comment can't be longer than 1024 characters");var e={user_id:t.userId,spend_id:t.spendId,asset:t.asset,amount:"number"==typeof t.amount?""+t.amount:t.amount};return void 0!==t.disableSendNotification&&(e.disable_send_notification=t.disableSendNotification),void 0!==t.comment&&(e.comment=t.comment),e}(t)).then((function(t){return d(t)}))},s.prototype.createInvoice=function(e){return this._transport.call("createInvoice",function(e){if(void 0!==e.description&&e.description.length>1024)throw new Error("Description can't be longer than 1024 characters");if(void 0!==e.paidBtnName&&!e.paidBtnUrl)throw new Error("Require paidBtnUrl parameter if paidBtnName parameter pass");if(void 0!==e.hiddenMessage&&e.hiddenMessage.length>2048)throw new Error("Hidden message can't be longer than 2048 characters");if(void 0!==e.expiresIn&&("number"!=typeof e.expiresIn||e.expiresIn<1||e.expiresIn>2678400))throw new Error("Expires must be a number between 1-2678400");var n;if(void 0!==e.payload&&(n="string"==typeof e.payload?e.payload:JSON.stringify(e.payload)).length>4096)throw new Error("Payload can't be longer than 4096 characters");var r={amount:"number"==typeof e.amount?""+e.amount:e.amount},o=e.currencyType||t.Crypto;if(r.currency_type=o,o===t.Crypto){var i=e.asset;if(!i)throw new Error("Field `asset` required for crypto currency type");r.asset=i}if(o===t.Fiat){var a=e.fiat;if(!a)throw new Error("Field `fiat` required for fiat currency type");if(r.fiat=a,void 0!==e.acceptedAssets){if(!Array.isArray(e.acceptedAssets))throw new Error("Field `acceptedAssets` must be array");r.accepted_assets=e.acceptedAssets.join(",")}}return void 0!==e.expiresIn&&(r.expires_in=e.expiresIn),void 0!==e.description&&(r.description=e.description),void 0!==e.hiddenMessage&&(r.hidden_message=e.hiddenMessage),void 0!==n&&(r.payload=n),void 0!==e.paidBtnUrl&&(r.paid_btn_url=e.paidBtnUrl),void 0!==e.paidBtnName&&(r.paid_btn_name=e.paidBtnName),void 0!==e.isAllowComments&&(r.allow_comments=e.isAllowComments),void 0!==e.isAllowAnonymous&&(r.allow_anonymous=e.isAllowAnonymous),r}(e)).then((function(t){return c(t)}))},s.prototype.createCheck=function(t){return this._transport.call("createCheck",function(t){var e={asset:t.asset,amount:"number"==typeof t.amount?""+t.amount:t.amount};if(void 0!==t.pinToUserId&&(e.pin_to_user_id=t.pinToUserId),void 0!==t.pinToUsername&&(e.pin_to_username=t.pinToUsername),void 0!==t.pinToUserId&&void 0!==t.pinToUsername)throw new Error("Pass only one of `pinToUserId` and `pinToUsername`");return e}(t)).then((function(t){return u(t)}))},s.prototype.deleteInvoice=function(t){return this._transport.call("deleteInvoice",{invoice_id:h(t)})},s.prototype.deleteCheck=function(t){return this._transport.call("deleteCheck",{check_id:h(t)})},s.prototype.getInvoices=function(t){return void 0===t&&(t={}),this._transport.call("getInvoices",function(t){var e={};return void 0!==t.status&&(e.status=t.status),void 0!==t.offset&&(e.offset=t.offset),void 0!==t.count&&(e.count=t.count),void 0!==t.asset&&(e.asset=t.asset),void 0!==t.fiat&&(e.fiat=t.fiat),void 0!==t.ids&&(e.invoice_ids=t.ids.join(",")),e}(t)).then((function(t){return p(t)}))},s.prototype.getInvoicesPaginate=function(t){void 0===t&&(t={});var e=function(t,e){var n={};void 0!==e.status&&(n.status=e.status),void 0!==e.asset&&(n.asset=e.asset),void 0!==e.fiat&&(n.fiat=e.fiat),void 0!==e.ids&&(n.invoice_ids=e.ids.join(","));var r=e.page?+e.page:1;return r<1&&(r=1),n.count=t,n.offset=t*(r-1),n}(this._pageSize,t);return this._transport.call("getInvoices",e).then((function(t){return p(t)}))},s.prototype.getChecks=function(t){return void 0===t&&(t={}),this._transport.call("getChecks",function(t){var e={};return void 0!==t.status&&(e.status=t.status),void 0!==t.offset&&(e.offset=t.offset),void 0!==t.count&&(e.count=t.count),void 0!==t.asset&&(e.asset=t.asset),void 0!==t.ids&&(e.check_ids=t.ids.join(",")),e}(t)).then((function(t){return f(t)}))},s.prototype.getChecksPaginate=function(t){void 0===t&&(t={});var e=function(t,e){var n={};void 0!==e.status&&(n.status=e.status),void 0!==e.asset&&(n.asset=e.asset),void 0!==e.ids&&(n.check_ids=e.ids.join(","));var r=e.page?+e.page:1;return r<1&&(r=1),n.count=t,n.offset=t*(r-1),n}(this._pageSize,t);return this._transport.call("getChecks",e).then((function(t){return f(t)}))},s.prototype.getTransfers=function(t){return void 0===t&&(t={}),this._transport.call("getTransfers",function(t){var e={};return void 0!==t.offset&&(e.offset=t.offset),void 0!==t.count&&(e.count=t.count),void 0!==t.asset&&(e.asset=t.asset),void 0!==t.spendId&&(e.spend_id=t.spendId),void 0!==t.ids&&(e.transfer_ids=t.ids.join(",")),e}(t)).then((function(t){return l(t)}))},s.prototype.getTransfersPaginate=function(t){void 0===t&&(t={});var e=function(t,e){var n={};void 0!==e.asset&&(n.asset=e.asset),void 0!==e.spendId&&(n.spend_id=e.spendId),void 0!==e.ids&&(n.transfer_ids=e.ids.join(","));var r=e.page?+e.page:1;return r<1&&(r=1),n.count=t,n.offset=t*(r-1),n}(this._pageSize,t);return this._transport.call("getTransfers",e).then((function(t){return l(t)}))},s.prototype.call=function(t,e){return void 0===e&&(e={}),this._transport.call(t,e)},s.CurrencyType=t,s.DetailedCurrencyType=e,s.InvoiceStatus=n,s.CheckStatus=r,s.TransferStatus=o,s}(function(){function t(t,e){void 0===e&&(e="mainnet"),this._transport=new y(t,e)}return t.prototype.getCurrencies=function(e){return this._currenciesFetchHandler||(this._currenciesFetchHandler=m(this._transport,"getCurrencies",a,t._CURRENCIES_UPDATE_PERIOD)),this._currenciesFetchHandler(e)},t.prototype.getExchangeRates=function(e){return this._exchangeRatesFetchHandler||(this._exchangeRatesFetchHandler=m(this._transport,"getExchangeRates",s,t._EXCHANGE_RATES_UPDATE_PERIOD)),this._exchangeRatesFetchHandler(e)},t.prototype.getMe=function(e){return void 0===e&&(e=!1),this._meFetchHandler||(this._meFetchHandler=m(this._transport,"getMe",_,t._ME_UPDATE_PERIOD)),this._meFetchHandler(e)},t._CURRENCIES_UPDATE_PERIOD=3600,t._EXCHANGE_RATES_UPDATE_PERIOD=60,t._ME_UPDATE_PERIOD=3600,t}());return g}(); 2 | //# sourceMappingURL=crypto-bot-api.min.js.map 3 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslint from '@eslint/js'; 2 | import stylistic from '@stylistic/eslint-plugin'; 3 | import tseslint from 'typescript-eslint'; 4 | import tsdoc from 'eslint-plugin-tsdoc'; 5 | 6 | export default tseslint.config( 7 | eslint.configs.recommended, 8 | stylistic.configs['recommended-flat'], 9 | ...tseslint.configs.recommended, 10 | { 11 | plugins: { 12 | stylistic, 13 | tsdoc, 14 | }, 15 | rules: { 16 | '@typescript-eslint/no-explicit-any': 'off', 17 | 'tsdoc/syntax': 'warn', 18 | '@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: true }], 19 | '@stylistic/no-extra-semi': 'error', 20 | '@stylistic/semi': ['error', 'always'], 21 | '@stylistic/semi-style': ['error', 'last'], 22 | '@stylistic/member-delimiter-style': [ 23 | 'error', 24 | { 25 | multiline: { 26 | delimiter: 'comma', 27 | requireLast: true, 28 | }, 29 | singleline: { 30 | delimiter: 'comma', 31 | requireLast: false, 32 | }, 33 | multilineDetection: 'brackets', 34 | }, 35 | ], 36 | '@stylistic/quote-props': ['error', 'as-needed'], 37 | }, 38 | }, 39 | { 40 | ignores: [ 41 | 'dist/', 42 | 'docs/', 43 | 'examples/', 44 | 'lib/', 45 | ], 46 | }, 47 | ); 48 | -------------------------------------------------------------------------------- /examples/create-invoice.js: -------------------------------------------------------------------------------- 1 | const CrypoBotAPI = require('crypto-bot-api'); 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | 5 | // Only required parameters 6 | client.createInvoice({ 7 | amount: 9.5, 8 | asset: 'TON' 9 | }).then(invoice => { 10 | // Send invoice.payUrl to user 11 | console.log(invoice); 12 | }); 13 | 14 | // With pay in any support crypto currency using exchange rate passed fiat currency 15 | client.createInvoice({ 16 | amount: 99, 17 | currencyType: CrypoBotAPI.CurrencyType.Fiat, 18 | fiat: 'USD' 19 | }).then(invoice => { 20 | // Send invoice.payUrl to user 21 | console.log(invoice); 22 | }); 23 | 24 | // With pay in passed crypto currencies using exchange rate passed fiat currency 25 | client.createInvoice({ 26 | amount: 99, 27 | currencyType: CrypoBotAPI.CurrencyType.Fiat, 28 | fiat: 'USD', 29 | acceptedAssets: ['TON', 'BTC'] 30 | }).then(invoice => { 31 | // Send invoice.payUrl to user 32 | console.log(invoice); 33 | }); 34 | 35 | // With description, payload and anonymous payment disabled 36 | client.createInvoice({ 37 | amount: 9.5, 38 | asset: 'TON', 39 | description: 'Pay order in SuperShop', 40 | isAllowAnonymous: false, 41 | payload: 'order12345' 42 | }).then(invoice => { 43 | // Send invoice.payUrl to user 44 | console.log(invoice); 45 | }); 46 | 47 | // With pay button and payment comment disabled 48 | client.createInvoice({ 49 | amount: 9.5, 50 | asset: 'TON', 51 | paidBtnName: 'viewItem', 52 | paidBtnUrl: 'https://supershop.com/order12345', 53 | isAllowComments: false, 54 | }).then(invoice => { 55 | // Send invoice.payUrl to user 56 | console.log(invoice); 57 | }); -------------------------------------------------------------------------------- /examples/create-invoice.ts: -------------------------------------------------------------------------------- 1 | import CrypoBotAPI, { type CreateInvoiceOptions } from 'crypto-bot-api'; 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | 5 | // Only required parameters 6 | const minimalOptions: CreateInvoiceOptions = { 7 | amount: 9.5, 8 | asset: 'TON' 9 | }; 10 | client.createInvoice(minimalOptions).then(invoice => { 11 | // Send invoice.payUrl to user 12 | console.log(invoice); 13 | }); 14 | 15 | // With pay in any support crypto currency using exchange rate passed fiat currency 16 | const fiatOptions: CreateInvoiceOptions = { 17 | amount: 99, 18 | currencyType: CrypoBotAPI.CurrencyType.Fiat, 19 | fiat: 'USD' 20 | } 21 | client.createInvoice(fiatOptions).then(invoice => { 22 | // Send invoice.payUrl to user 23 | console.log(invoice); 24 | }); 25 | 26 | // With pay in passed crypto currencies using exchange rate passed fiat currency 27 | const fiatLimitedOptions: CreateInvoiceOptions = { 28 | amount: 99, 29 | currencyType: CrypoBotAPI.CurrencyType.Fiat, 30 | fiat: 'USD', 31 | acceptedAssets: ['TON', 'BTC'] 32 | }; 33 | client.createInvoice(fiatLimitedOptions).then(invoice => { 34 | // Send invoice.payUrl to user 35 | console.log(invoice); 36 | }); 37 | 38 | // With description, payload and anonymous payment disabled 39 | const custom1Options: CreateInvoiceOptions = { 40 | amount: 9.5, 41 | asset: 'TON', 42 | description: 'Pay order in SuperShop', 43 | isAllowAnonymous: false, 44 | payload: 'order12345' 45 | }; 46 | client.createInvoice(custom1Options).then(invoice => { 47 | // Send invoice.payUrl to user 48 | console.log(invoice); 49 | }); 50 | 51 | // With pay button and payment comment disabled 52 | const custom2Options: CreateInvoiceOptions = { 53 | amount: 9.5, 54 | asset: 'TON', 55 | paidBtnName: 'viewItem', 56 | paidBtnUrl: 'https://supershop.com/order12345', 57 | isAllowComments: false, 58 | }; 59 | client.createInvoice(custom2Options).then(invoice => { 60 | // Send invoice.payUrl to user 61 | console.log(invoice); 62 | }); -------------------------------------------------------------------------------- /examples/exchange-rate-ticker.js: -------------------------------------------------------------------------------- 1 | const CrypoBotAPI = require('crypto-bot-api'); 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | 5 | const tick = async () => { 6 | setTimeout(tick, 15000); 7 | 8 | // Get exchange rate with isForce parameter, because example update 9 | // interval (15s) less than library exchange rates cache 10 | // interval (60s) 11 | const rate = await client.getExchangeRate('TON', 'USD', true); 12 | console.log('TON to USD', rate); 13 | }; 14 | 15 | tick(); -------------------------------------------------------------------------------- /examples/exchange-rate-ticker.ts: -------------------------------------------------------------------------------- 1 | import CrypoBotAPI from 'crypto-bot-api'; 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | 5 | const tick = async () => { 6 | setTimeout(tick, 15000); 7 | 8 | // Get exchange rate with isForce parameter, because example update 9 | // interval (15s) less than library exchange rates cache 10 | // interval (60s) 11 | const rate = await client.getExchangeRate('TON', 'USD', true); 12 | console.log('TON to USD', rate); 13 | }; 14 | 15 | tick(); -------------------------------------------------------------------------------- /examples/get-app-information.js: -------------------------------------------------------------------------------- 1 | const CrypoBotAPI = require('crypto-bot-api'); 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | 5 | client.getMe().then(me => console.log(me)); -------------------------------------------------------------------------------- /examples/get-app-information.ts: -------------------------------------------------------------------------------- 1 | import CrypoBotAPI from 'crypto-bot-api'; 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | 5 | const me = await client.getMe(); 6 | console.log(me) -------------------------------------------------------------------------------- /examples/get-invoices-paginate.js: -------------------------------------------------------------------------------- 1 | const CrypoBotAPI = require('crypto-bot-api'); 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | client.setPageSize(20); 5 | 6 | client.getInvoicesPaginate({ asset: 'BTC', page: 2 }).then(invoices => console.log(invoices)); -------------------------------------------------------------------------------- /examples/get-invoices-paginate.ts: -------------------------------------------------------------------------------- 1 | import CrypoBotAPI from 'crypto-bot-api'; 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | client.setPageSize(20); 5 | 6 | const invoices = await client.getInvoicesPaginate({ asset: 'BTC', page: 2 }); 7 | console.log(invoices); -------------------------------------------------------------------------------- /examples/get-invoices.js: -------------------------------------------------------------------------------- 1 | const CrypoBotAPI = require('crypto-bot-api'); 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | 5 | // Without filters 6 | client.getInvoices() 7 | .then(invoices => console.log(invoices)); 8 | 9 | // With filters 10 | client.getInvoices({ asset: 'TON', status: CrypoBotAPI.InvoiceStatus.Active, count: 5 }) 11 | .then(invoices => console.log(invoices)); -------------------------------------------------------------------------------- /examples/get-invoices.ts: -------------------------------------------------------------------------------- 1 | import CrypoBotAPI, { InvoiceStatus } from 'crypto-bot-api'; 2 | 3 | const client = new CrypoBotAPI('1234:AAA...AAA'); 4 | 5 | // Without filters 6 | const invoices1 = await client.getInvoices(); 7 | console.log(invoices1); 8 | 9 | // With filters 10 | const invoices2 = await client.getInvoices({ 11 | asset: 'TON', 12 | status: InvoiceStatus.Active, 13 | count: 5 14 | }); 15 | console.log(invoices2); 16 | -------------------------------------------------------------------------------- /examples/webhooks-express-receiving.js: -------------------------------------------------------------------------------- 1 | const { readFileSync } = require('fs'); 2 | const { createServer } = require('https'); 3 | const express = require('express'); 4 | const CrypoBotAPI = require('crypto-bot-api'); 5 | 6 | const app = express(); 7 | const client = new CrypoBotAPI('1234:AAA...AAA'); 8 | 9 | client.on('paid', (invoice, requestDate) => { 10 | console.log(requestDate, invoice); 11 | }); 12 | 13 | app.use('/secret-webhooks-path', client.middleware()); 14 | 15 | // Note: if you want to use self-signed certificate 16 | // you must uploat it in CryptoBot API application settings 17 | createServer({ 18 | key: readFileSync(__dirname + '/server.key'), 19 | cert: readFileSync(__dirname + '/server.cert') 20 | }, app).listen(443); -------------------------------------------------------------------------------- /examples/webhooks-express-receiving.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | import { createServer } from 'https'; 3 | // @ts-ignore 4 | import express from 'express'; 5 | import CrypoBotAPI from 'crypto-bot-api'; 6 | 7 | const app = express(); 8 | const client = new CrypoBotAPI('1234:AAA...AAA'); 9 | 10 | client.on('paid', (invoice, requestDate) => { 11 | console.log(requestDate, invoice); 12 | }); 13 | 14 | app.use('/secret-webhooks-path', client.middleware()); 15 | 16 | // Note: if you want to use self-signed certificate 17 | // you must uploat it in CryptoBot API application settings 18 | createServer({ 19 | key: readFileSync(__dirname + '/server.key'), 20 | cert: readFileSync(__dirname + '/server.cert') 21 | }, app).listen(443); -------------------------------------------------------------------------------- /examples/webhooks-receiving.js: -------------------------------------------------------------------------------- 1 | const { readFileSync } = require('fs'); 2 | const CrypoBotAPI = require('crypto-bot-api'); 3 | 4 | const client = new CrypoBotAPI('1234:AAA...AAA'); 5 | 6 | const onPaid = (invoice, requestDate) => { 7 | console.log(requestDate, invoice); 8 | }; 9 | 10 | // If you app work behind proxy and no need create HTTPS server, 11 | // no pass `key` and `cert` fields and add `http` field with `true` value: 12 | // { http: true } 13 | // 14 | // Note: if you want to use self-signed certificate 15 | // you must uploat it in CryptoBot API application settings 16 | client.createServer({ 17 | key: readFileSync(__dirname + '/server.key'), 18 | cert: readFileSync(__dirname + '/server.cert') 19 | }, '/secret-webhooks-path') 20 | .then(() => client.on('paid', onPaid)) 21 | .catch(err => console.log('Create server error:', err)); -------------------------------------------------------------------------------- /examples/webhooks-receiving.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | import CrypoBotAPI from 'crypto-bot-api'; 3 | 4 | const client = new CrypoBotAPI('1234:AAA...AAA'); 5 | 6 | // If you app work behind proxy and no need create HTTPS server, 7 | // no pass `key` and `cert` fields and add `http` field with `true` value: 8 | // { http: true } 9 | // 10 | // Note: if you want to use self-signed certificate 11 | // you must uploat it in CryptoBot API application settings 12 | try { 13 | await client.createServer({ 14 | key: readFileSync(__dirname + '/server.key'), 15 | cert: readFileSync(__dirname + '/server.cert') 16 | }, '/secret-webhooks-path'); 17 | } catch (err) { 18 | console.log('Create server error:', err); 19 | process.exit(); 20 | } 21 | 22 | client.on('paid', (invoice, requestDate) => { 23 | console.log(requestDate, invoice); 24 | }); -------------------------------------------------------------------------------- /lib/classes/Client.d.ts: -------------------------------------------------------------------------------- 1 | import { Balances, Balance, BalancesType, Currency, CurrencyCode, CryptoCurrencyCode, CurrencyType, DetailedCurrencyType, Invoice, Check, Stats, Transfer, InvoiceStatus, CheckStatus, TransferStatus } from '../helpers/casts'; 2 | import { CreateInvoiceOptions, CreateCheckOptions, GetChecksOptions, GetChecksPaginateOptions, GetInvoicesOptions, GetInvoicesPaginateOptions, GetStatsOptions, TransferOptions, GetTransfersOptions, GetTransfersPaginateOptions } from '../helpers/utils'; 3 | import Store from './Store'; 4 | /** 5 | * Main class for work with API for browsers 6 | * 7 | * Library for browsers default export this class 8 | * 9 | * @category External 10 | */ 11 | export default class Client extends Store { 12 | /** Page size for {@link Client.getInvoicesPaginate} method */ 13 | private _pageSize; 14 | /** 15 | * Access to {@link CurrencyType} enumeration, used in {@link Invoice} type 16 | */ 17 | static CurrencyType: typeof CurrencyType; 18 | /** 19 | * Access to {@link DetailedCurrencyType} enumeration, used in {@link Store.getCurrencies} 20 | * and {@link Client.getCurrency} methods results 21 | */ 22 | static DetailedCurrencyType: typeof DetailedCurrencyType; 23 | /** 24 | * Access to {@link InvoiceStatus} enumeration, used in {@link Invoice} type, 25 | * {@link Client.getInvoices} and {@link Client.getInvoicesPaginate} methods options 26 | */ 27 | static InvoiceStatus: typeof InvoiceStatus; 28 | /** 29 | * Access to {@link CheckStatus} enumeration, used in {@link Check} type, 30 | * {@link Client.getChecks} and {@link Client.getChecksPaginate} methods options 31 | */ 32 | static CheckStatus: typeof CheckStatus; 33 | /** 34 | * Access to {@link TransferStatus} enumeration, used in {@link Transfer} type, 35 | * {@link Client.getTransfers} and {@link Client.getTransfersPaginate} methods options 36 | */ 37 | static TransferStatus: typeof TransferStatus; 38 | /** 39 | * Return count invoices per page for {@link Client.getInvoicesPaginate} method 40 | */ 41 | getPageSize(): number; 42 | /** 43 | * Set count invoices per page for {@link Client.getInvoicesPaginate} method 44 | * 45 | * @param pageSizeParam - Invoices per page 46 | * 47 | * @throws Error - If `pageSize` parameter is invalid 48 | */ 49 | setPageSize(pageSizeParam: number): void; 50 | /** 51 | * Get associated with passed API key app statistics 52 | * 53 | * Use {@link toStats} backend API result convert function 54 | * 55 | * @param options - New receive statistics options 56 | * 57 | * @throws Error - If there is an error sending request to backend API or parsing response 58 | * 59 | * @returns Promise, what resolved to associated with passed API key app statistics object 60 | */ 61 | getStats(options?: GetStatsOptions): Promise; 62 | /** 63 | * Get API app balances infomation 64 | * 65 | * Use {@link toBalances} backend API result convert function 66 | * 67 | * @throws Error - If there is an error sending request to backend API or parsing response 68 | * 69 | * @returns Promise, what resolved to API app balances infomation object 70 | */ 71 | getBalances(): Promise; 72 | /** 73 | * Get API app balances infomation 74 | * 75 | * Use {@link toBalances} backend API result convert function 76 | * 77 | * @throws Error - If there is an error sending request to backend API or parsing response 78 | * 79 | * @returns Promise, what resolved to API app available balances infomation object 80 | */ 81 | getBalancesAvailable(): Promise; 82 | /** 83 | * Get API app balances infomation 84 | * 85 | * Use {@link toBalances} backend API result convert function 86 | * 87 | * @throws Error - If there is an error sending request to backend API or parsing response 88 | * 89 | * @returns Promise, what resolved to API app balances on hold infomation object 90 | */ 91 | getBalancesOnhold(): Promise; 92 | /** 93 | * Get API app balance value for passed currency 94 | * 95 | * Call {@link Client.getBalances} method to fetch balances information 96 | * 97 | * @param currencyCode - Crypto currency code 98 | * 99 | * @throws Error - If there is an error sending request to backend API or parsing response 100 | * 101 | * @returns Promise, what resolved to API app balance value for passed currency 102 | */ 103 | getBalance(currencyCode: CryptoCurrencyCode): Promise; 104 | /** 105 | * Get API app balance value for passed currency 106 | * 107 | * Call {@link Client.getBalances} method to fetch balances information 108 | * 109 | * @param currencyCode - Crypto currency code 110 | * 111 | * @throws Error - If there is an error sending request to backend API or parsing response 112 | * 113 | * @returns Promise, what resolved to API app available balance value for passed currency 114 | */ 115 | getBalanceAvailable(currencyCode: CryptoCurrencyCode): Promise; 116 | /** 117 | * Get API app balance value for passed currency 118 | * 119 | * Call {@link Client.getBalances} method to fetch balances information 120 | * 121 | * @param currencyCode - Crypto currency code 122 | * 123 | * @throws Error - If there is an error sending request to backend API or parsing response 124 | * 125 | * @returns Promise, what resolved to API app balance on hold value for passed currency 126 | */ 127 | getBalanceOnhold(currencyCode: CryptoCurrencyCode): Promise; 128 | /** 129 | * Get currency with passed code infomation 130 | * 131 | * Call {@link Store.getCurrencies} method to fetch currencies information 132 | * 133 | * @param currencyCode - Currency code 134 | * @param isForce - If true, return fresh data from backend API, not from cache 135 | * 136 | * @throws Error - If there is an error sending request to backend API or parsing response 137 | * 138 | * @returns Promise, what resolved to currency with passed code infomation object 139 | * or null, if currency with passed code not exists 140 | */ 141 | getCurrency(currencyCode: CurrencyCode, isForce?: boolean): Promise; 142 | /** 143 | * Get one exchange rate infomation to passed currencies pair 144 | * 145 | * Call {@link Store.getExchangeRates} method to fetch exchange rates information, 146 | * {@link Store.getCurrencies} method to fetch currencies information 147 | * and use {@link getExchageRate} function to get signle exchange rate 148 | * 149 | * @param source - Source currency code 150 | * @param target - Target currency code 151 | * @param isForce - If true, return fresh data from backend API, not from cache 152 | * 153 | * @throws Error - If there is an error sending request to backend API or parsing response 154 | * 155 | * @returns Promise, what resolved to exchange rate or zero, if currencies pair not exists 156 | */ 157 | getExchangeRate(source: string, target: string, isForce?: boolean): Promise; 158 | /** 159 | * Transfer 160 | * 161 | * Use {@link toTransfer} backend API result convert function and 162 | * prepare backend API parameters {@link prepareTransferOptions} function 163 | * 164 | * @param options - Transfer options 165 | * 166 | * @throws Error - If there is an error sending request to backend API, parsing response error 167 | * or options object is invalid 168 | * 169 | * @returns Promise, what resolved to completed transfer information object 170 | */ 171 | transfer(options: TransferOptions): Promise; 172 | /** 173 | * Create invoice 174 | * 175 | * Use {@link toInvoice} backend API result convert function and 176 | * prepare backend API parameters {@link prepareCreateInvoiceOptions} function 177 | * 178 | * @param options - New invoice options 179 | * 180 | * @throws Error - If there is an error sending request to backend API, parsing response error 181 | * or options object is invalid 182 | * 183 | * @returns Promise, what resolved to created invoice information object 184 | */ 185 | createInvoice(options: CreateInvoiceOptions): Promise; 186 | /** 187 | * Create check 188 | * 189 | * Use {@link toCheck} backend API result convert function and 190 | * prepare backend API parameters {@link prepareCreateCheckOptions} function 191 | * 192 | * @param options - New check options 193 | * 194 | * @throws Error - If there is an error sending request to backend API, parsing response error 195 | * or options object is invalid 196 | * 197 | * @returns Promise, what resolved to created check information object 198 | */ 199 | createCheck(options: CreateCheckOptions): Promise; 200 | /** 201 | * Delete invoice 202 | * 203 | * @param id - Invoice identifier 204 | * 205 | * @throws Error - If there is an error sending request to backend API or parsing response error 206 | * 207 | * @returns Promise, what resolved to boolean operation result status 208 | */ 209 | deleteInvoice(id: number): Promise; 210 | /** 211 | * Delete check 212 | * 213 | * @param id - Check identifier 214 | * 215 | * @throws Error - If there is an error sending request to backend API or parsing response error 216 | * 217 | * @returns Promise, what resolved to boolean operation result status 218 | */ 219 | deleteCheck(id: number): Promise; 220 | /** 221 | * Get invoices 222 | * 223 | * Use {@link toInvoices} backend API result convert function and 224 | * prepare backend API parameters {@link prepareGetInvoicesOptions} function 225 | * 226 | * @param options - Filters options 227 | * 228 | * @throws Error - If there is an error sending request to backend API or parsing response 229 | * 230 | * @returns Promise, what resolved to invoices information object 231 | */ 232 | getInvoices(options?: GetInvoicesOptions): Promise; 233 | /** 234 | * Get invoices paginated 235 | * 236 | * Fetch invoices with `page` options parameter, except `count` and `offset` 237 | * 238 | * See {@link Client.getPageSize} and {@link Client.setPageSize} 239 | * 240 | * Use {@link toInvoices} backend API result convert function and 241 | * prepare backend API parameters {@link prepareGetInvoicesPaginateOptions} function 242 | * 243 | * @param options - Filters options 244 | * 245 | * @throws Error - If there is an error sending request to backend API, parsing response error 246 | * or options object is invalid 247 | * 248 | * @returns Promise, what resolved to invoices information object 249 | */ 250 | getInvoicesPaginate(options?: GetInvoicesPaginateOptions): Promise; 251 | /** 252 | * Get checks 253 | * 254 | * Use {@link toChecks} backend API result convert function and 255 | * prepare backend API parameters {@link prepareGetChecksOptions} function 256 | * 257 | * @param options - Filters options 258 | * 259 | * @throws Error - If there is an error sending request to backend API or parsing response 260 | * 261 | * @returns Promise, what resolved to checks information object 262 | */ 263 | getChecks(options?: GetChecksOptions): Promise; 264 | /** 265 | * Get checks paginated 266 | * 267 | * Fetch checks with `page` options parameter, except `count` and `offset` 268 | * 269 | * See {@link Client.getPageSize} and {@link Client.setPageSize} 270 | * 271 | * Use {@link toChecks} backend API result convert function and 272 | * prepare backend API parameters {@link prepareGetChecksPaginateOptions} function 273 | * 274 | * @param options - Filters options 275 | * 276 | * @throws Error - If there is an error sending request to backend API, parsing response error 277 | * or options object is invalid 278 | * 279 | * @returns Promise, what resolved to checks information object 280 | */ 281 | getChecksPaginate(options?: GetChecksPaginateOptions): Promise; 282 | /** 283 | * Get transfers 284 | * 285 | * Use {@link toTransfers} backend API result convert function and 286 | * prepare backend API parameters {@link prepareGetTransfersOptions} function 287 | * 288 | * @param options - Filters options 289 | * 290 | * @throws Error - If there is an error sending request to backend API or parsing response 291 | * 292 | * @returns Promise, what resolved to transfers information object 293 | */ 294 | getTransfers(options?: GetTransfersOptions): Promise; 295 | /** 296 | * Get transfers paginated 297 | * 298 | * Fetch checks with `page` options parameter, except `count` and `offset` 299 | * 300 | * See {@link Client.getPageSize} and {@link Client.setPageSize} 301 | * 302 | * Use {@link toTransfers} backend API result convert function and 303 | * prepare backend API parameters {@link prepareGetTransfersOptions} function 304 | * 305 | * @param options - Filters options 306 | * 307 | * @throws Error - If there is an error sending request to backend API, parsing response error 308 | * or options object is invalid 309 | * 310 | * @returns Promise, what resolved to transfers information object 311 | */ 312 | getTransfersPaginate(options?: GetTransfersPaginateOptions): Promise; 313 | /** 314 | * Call backend API method directly (types unsafe) 315 | * 316 | * Use it if backend API update (add new methods, change request or response fileds), 317 | * but library is not 318 | * 319 | * @param method - Backend API method name 320 | * @param options - Backend API options object 321 | * 322 | * @throws Error - If there is an error sending request to backend API or parsing response 323 | * 324 | * @returns Promise, what resolved to backend API response `result` field value 325 | */ 326 | call(method: string, options?: object): Promise; 327 | } 328 | -------------------------------------------------------------------------------- /lib/classes/Client.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const casts_1 = require("../helpers/casts"); 4 | const utils_1 = require("../helpers/utils"); 5 | const Store_1 = require("./Store"); 6 | // Because `tsdoc` not support `@category` tag, but `typedoc` support 7 | /* eslint-disable tsdoc/syntax */ 8 | /** 9 | * Main class for work with API for browsers 10 | * 11 | * Library for browsers default export this class 12 | * 13 | * @category External 14 | */ 15 | /* eslint-enable tsdoc/syntax */ 16 | class Client extends Store_1.default { 17 | constructor() { 18 | super(...arguments); 19 | /** Page size for {@link Client.getInvoicesPaginate} method */ 20 | this._pageSize = 100; 21 | } 22 | /** 23 | * Return count invoices per page for {@link Client.getInvoicesPaginate} method 24 | */ 25 | getPageSize() { 26 | return this._pageSize; 27 | } 28 | /** 29 | * Set count invoices per page for {@link Client.getInvoicesPaginate} method 30 | * 31 | * @param pageSizeParam - Invoices per page 32 | * 33 | * @throws Error - If `pageSize` parameter is invalid 34 | */ 35 | setPageSize(pageSizeParam) { 36 | const pageSize = +pageSizeParam; 37 | if (pageSize > 1000 || pageSize < 1) { 38 | throw Error('Page size may be from 1 to 1000'); 39 | } 40 | this._pageSize = pageSize; 41 | } 42 | /** 43 | * Get associated with passed API key app statistics 44 | * 45 | * Use {@link toStats} backend API result convert function 46 | * 47 | * @param options - New receive statistics options 48 | * 49 | * @throws Error - If there is an error sending request to backend API or parsing response 50 | * 51 | * @returns Promise, what resolved to associated with passed API key app statistics object 52 | */ 53 | getStats(options = {}) { 54 | return this._transport.call('getStats', (0, utils_1.prepareGetStatsOptions)(options)) 55 | .then((result) => (0, casts_1.toStats)(result)); 56 | } 57 | /** 58 | * Get API app balances infomation 59 | * 60 | * Use {@link toBalances} backend API result convert function 61 | * 62 | * @throws Error - If there is an error sending request to backend API or parsing response 63 | * 64 | * @returns Promise, what resolved to API app balances infomation object 65 | */ 66 | getBalances() { 67 | return this._transport.call('getBalance').then((result) => (0, casts_1.toBalances)(result)); 68 | } 69 | /** 70 | * Get API app balances infomation 71 | * 72 | * Use {@link toBalances} backend API result convert function 73 | * 74 | * @throws Error - If there is an error sending request to backend API or parsing response 75 | * 76 | * @returns Promise, what resolved to API app available balances infomation object 77 | */ 78 | getBalancesAvailable() { 79 | return this.getBalances() 80 | .then((balances) => { 81 | return Object.entries(balances).reduce((accumulator, entry) => { 82 | const [code, balance] = entry; 83 | accumulator[code] = balance.available; 84 | return accumulator; 85 | }, {}); 86 | }); 87 | } 88 | /** 89 | * Get API app balances infomation 90 | * 91 | * Use {@link toBalances} backend API result convert function 92 | * 93 | * @throws Error - If there is an error sending request to backend API or parsing response 94 | * 95 | * @returns Promise, what resolved to API app balances on hold infomation object 96 | */ 97 | getBalancesOnhold() { 98 | return this.getBalances() 99 | .then((balances) => { 100 | return Object.entries(balances).reduce((accumulator, entry) => { 101 | const [code, balance] = entry; 102 | accumulator[code] = balance.onhold; 103 | return accumulator; 104 | }, {}); 105 | }); 106 | } 107 | /** 108 | * Get API app balance value for passed currency 109 | * 110 | * Call {@link Client.getBalances} method to fetch balances information 111 | * 112 | * @param currencyCode - Crypto currency code 113 | * 114 | * @throws Error - If there is an error sending request to backend API or parsing response 115 | * 116 | * @returns Promise, what resolved to API app balance value for passed currency 117 | */ 118 | getBalance(currencyCode) { 119 | return this.getBalances() 120 | .then((balances) => { 121 | if (balances[currencyCode] === undefined) 122 | return { available: '0', onhold: '0' }; 123 | return balances[currencyCode]; 124 | }); 125 | } 126 | /** 127 | * Get API app balance value for passed currency 128 | * 129 | * Call {@link Client.getBalances} method to fetch balances information 130 | * 131 | * @param currencyCode - Crypto currency code 132 | * 133 | * @throws Error - If there is an error sending request to backend API or parsing response 134 | * 135 | * @returns Promise, what resolved to API app available balance value for passed currency 136 | */ 137 | getBalanceAvailable(currencyCode) { 138 | return this.getBalances() 139 | .then((balances) => { 140 | if (balances[currencyCode] === undefined) 141 | return '0'; 142 | return balances[currencyCode].available; 143 | }); 144 | } 145 | /** 146 | * Get API app balance value for passed currency 147 | * 148 | * Call {@link Client.getBalances} method to fetch balances information 149 | * 150 | * @param currencyCode - Crypto currency code 151 | * 152 | * @throws Error - If there is an error sending request to backend API or parsing response 153 | * 154 | * @returns Promise, what resolved to API app balance on hold value for passed currency 155 | */ 156 | getBalanceOnhold(currencyCode) { 157 | return this.getBalances() 158 | .then((balances) => { 159 | if (balances[currencyCode] === undefined) 160 | return '0'; 161 | return balances[currencyCode].onhold; 162 | }); 163 | } 164 | /** 165 | * Get currency with passed code infomation 166 | * 167 | * Call {@link Store.getCurrencies} method to fetch currencies information 168 | * 169 | * @param currencyCode - Currency code 170 | * @param isForce - If true, return fresh data from backend API, not from cache 171 | * 172 | * @throws Error - If there is an error sending request to backend API or parsing response 173 | * 174 | * @returns Promise, what resolved to currency with passed code infomation object 175 | * or null, if currency with passed code not exists 176 | */ 177 | getCurrency(currencyCode, isForce = false) { 178 | return this.getCurrencies(isForce) 179 | .then((currencies) => { 180 | if (currencies[currencyCode] === undefined) 181 | return null; 182 | return currencies[currencyCode]; 183 | }); 184 | } 185 | /** 186 | * Get one exchange rate infomation to passed currencies pair 187 | * 188 | * Call {@link Store.getExchangeRates} method to fetch exchange rates information, 189 | * {@link Store.getCurrencies} method to fetch currencies information 190 | * and use {@link getExchageRate} function to get signle exchange rate 191 | * 192 | * @param source - Source currency code 193 | * @param target - Target currency code 194 | * @param isForce - If true, return fresh data from backend API, not from cache 195 | * 196 | * @throws Error - If there is an error sending request to backend API or parsing response 197 | * 198 | * @returns Promise, what resolved to exchange rate or zero, if currencies pair not exists 199 | */ 200 | getExchangeRate(source, target, isForce = false) { 201 | return this.getExchangeRates(isForce) 202 | .then((exchangeRates) => { 203 | return (0, utils_1.getExchageRate)(source, target, exchangeRates); 204 | }); 205 | } 206 | /** 207 | * Transfer 208 | * 209 | * Use {@link toTransfer} backend API result convert function and 210 | * prepare backend API parameters {@link prepareTransferOptions} function 211 | * 212 | * @param options - Transfer options 213 | * 214 | * @throws Error - If there is an error sending request to backend API, parsing response error 215 | * or options object is invalid 216 | * 217 | * @returns Promise, what resolved to completed transfer information object 218 | */ 219 | transfer(options) { 220 | return this._transport.call('transfer', (0, utils_1.prepareTransferOptions)(options)) 221 | .then((result) => (0, casts_1.toTransfer)(result)); 222 | } 223 | /** 224 | * Create invoice 225 | * 226 | * Use {@link toInvoice} backend API result convert function and 227 | * prepare backend API parameters {@link prepareCreateInvoiceOptions} function 228 | * 229 | * @param options - New invoice options 230 | * 231 | * @throws Error - If there is an error sending request to backend API, parsing response error 232 | * or options object is invalid 233 | * 234 | * @returns Promise, what resolved to created invoice information object 235 | */ 236 | createInvoice(options) { 237 | return this._transport.call('createInvoice', (0, utils_1.prepareCreateInvoiceOptions)(options)) 238 | .then((result) => (0, casts_1.toInvoice)(result)); 239 | } 240 | /** 241 | * Create check 242 | * 243 | * Use {@link toCheck} backend API result convert function and 244 | * prepare backend API parameters {@link prepareCreateCheckOptions} function 245 | * 246 | * @param options - New check options 247 | * 248 | * @throws Error - If there is an error sending request to backend API, parsing response error 249 | * or options object is invalid 250 | * 251 | * @returns Promise, what resolved to created check information object 252 | */ 253 | createCheck(options) { 254 | return this._transport.call('createCheck', (0, utils_1.prepareCreateCheckOptions)(options)) 255 | .then((result) => (0, casts_1.toCheck)(result)); 256 | } 257 | /** 258 | * Delete invoice 259 | * 260 | * @param id - Invoice identifier 261 | * 262 | * @throws Error - If there is an error sending request to backend API or parsing response error 263 | * 264 | * @returns Promise, what resolved to boolean operation result status 265 | */ 266 | deleteInvoice(id) { 267 | return this._transport.call('deleteInvoice', { invoice_id: (0, utils_1.prepareDeleteOptions)(id) }); 268 | } 269 | /** 270 | * Delete check 271 | * 272 | * @param id - Check identifier 273 | * 274 | * @throws Error - If there is an error sending request to backend API or parsing response error 275 | * 276 | * @returns Promise, what resolved to boolean operation result status 277 | */ 278 | deleteCheck(id) { 279 | return this._transport.call('deleteCheck', { check_id: (0, utils_1.prepareDeleteOptions)(id) }); 280 | } 281 | /** 282 | * Get invoices 283 | * 284 | * Use {@link toInvoices} backend API result convert function and 285 | * prepare backend API parameters {@link prepareGetInvoicesOptions} function 286 | * 287 | * @param options - Filters options 288 | * 289 | * @throws Error - If there is an error sending request to backend API or parsing response 290 | * 291 | * @returns Promise, what resolved to invoices information object 292 | */ 293 | getInvoices(options = {}) { 294 | return this._transport.call('getInvoices', (0, utils_1.prepareGetInvoicesOptions)(options)) 295 | .then((result) => (0, casts_1.toInvoices)(result)); 296 | } 297 | /** 298 | * Get invoices paginated 299 | * 300 | * Fetch invoices with `page` options parameter, except `count` and `offset` 301 | * 302 | * See {@link Client.getPageSize} and {@link Client.setPageSize} 303 | * 304 | * Use {@link toInvoices} backend API result convert function and 305 | * prepare backend API parameters {@link prepareGetInvoicesPaginateOptions} function 306 | * 307 | * @param options - Filters options 308 | * 309 | * @throws Error - If there is an error sending request to backend API, parsing response error 310 | * or options object is invalid 311 | * 312 | * @returns Promise, what resolved to invoices information object 313 | */ 314 | getInvoicesPaginate(options = {}) { 315 | const prepared = (0, utils_1.prepareGetInvoicesPaginateOptions)(this._pageSize, options); 316 | return this._transport.call('getInvoices', prepared) 317 | .then((result) => (0, casts_1.toInvoices)(result)); 318 | } 319 | /** 320 | * Get checks 321 | * 322 | * Use {@link toChecks} backend API result convert function and 323 | * prepare backend API parameters {@link prepareGetChecksOptions} function 324 | * 325 | * @param options - Filters options 326 | * 327 | * @throws Error - If there is an error sending request to backend API or parsing response 328 | * 329 | * @returns Promise, what resolved to checks information object 330 | */ 331 | getChecks(options = {}) { 332 | return this._transport.call('getChecks', (0, utils_1.prepareGetChecksOptions)(options)) 333 | .then((result) => (0, casts_1.toChecks)(result)); 334 | } 335 | /** 336 | * Get checks paginated 337 | * 338 | * Fetch checks with `page` options parameter, except `count` and `offset` 339 | * 340 | * See {@link Client.getPageSize} and {@link Client.setPageSize} 341 | * 342 | * Use {@link toChecks} backend API result convert function and 343 | * prepare backend API parameters {@link prepareGetChecksPaginateOptions} function 344 | * 345 | * @param options - Filters options 346 | * 347 | * @throws Error - If there is an error sending request to backend API, parsing response error 348 | * or options object is invalid 349 | * 350 | * @returns Promise, what resolved to checks information object 351 | */ 352 | getChecksPaginate(options = {}) { 353 | const prepared = (0, utils_1.prepareGetChecksPaginateOptions)(this._pageSize, options); 354 | return this._transport.call('getChecks', prepared) 355 | .then((result) => (0, casts_1.toChecks)(result)); 356 | } 357 | /** 358 | * Get transfers 359 | * 360 | * Use {@link toTransfers} backend API result convert function and 361 | * prepare backend API parameters {@link prepareGetTransfersOptions} function 362 | * 363 | * @param options - Filters options 364 | * 365 | * @throws Error - If there is an error sending request to backend API or parsing response 366 | * 367 | * @returns Promise, what resolved to transfers information object 368 | */ 369 | getTransfers(options = {}) { 370 | return this._transport.call('getTransfers', (0, utils_1.prepareGetTransfersOptions)(options)) 371 | .then((result) => (0, casts_1.toTransfers)(result)); 372 | } 373 | /** 374 | * Get transfers paginated 375 | * 376 | * Fetch checks with `page` options parameter, except `count` and `offset` 377 | * 378 | * See {@link Client.getPageSize} and {@link Client.setPageSize} 379 | * 380 | * Use {@link toTransfers} backend API result convert function and 381 | * prepare backend API parameters {@link prepareGetTransfersOptions} function 382 | * 383 | * @param options - Filters options 384 | * 385 | * @throws Error - If there is an error sending request to backend API, parsing response error 386 | * or options object is invalid 387 | * 388 | * @returns Promise, what resolved to transfers information object 389 | */ 390 | getTransfersPaginate(options = {}) { 391 | const prepared = (0, utils_1.prepareGetTransfersPaginateOptions)(this._pageSize, options); 392 | return this._transport.call('getTransfers', prepared) 393 | .then((result) => (0, casts_1.toTransfers)(result)); 394 | } 395 | /** 396 | * Call backend API method directly (types unsafe) 397 | * 398 | * Use it if backend API update (add new methods, change request or response fileds), 399 | * but library is not 400 | * 401 | * @param method - Backend API method name 402 | * @param options - Backend API options object 403 | * 404 | * @throws Error - If there is an error sending request to backend API or parsing response 405 | * 406 | * @returns Promise, what resolved to backend API response `result` field value 407 | */ 408 | call(method, options = {}) { 409 | return this._transport.call(method, options); 410 | } 411 | } 412 | /** 413 | * Access to {@link CurrencyType} enumeration, used in {@link Invoice} type 414 | */ 415 | Client.CurrencyType = casts_1.CurrencyType; 416 | /** 417 | * Access to {@link DetailedCurrencyType} enumeration, used in {@link Store.getCurrencies} 418 | * and {@link Client.getCurrency} methods results 419 | */ 420 | Client.DetailedCurrencyType = casts_1.DetailedCurrencyType; 421 | /** 422 | * Access to {@link InvoiceStatus} enumeration, used in {@link Invoice} type, 423 | * {@link Client.getInvoices} and {@link Client.getInvoicesPaginate} methods options 424 | */ 425 | Client.InvoiceStatus = casts_1.InvoiceStatus; 426 | /** 427 | * Access to {@link CheckStatus} enumeration, used in {@link Check} type, 428 | * {@link Client.getChecks} and {@link Client.getChecksPaginate} methods options 429 | */ 430 | Client.CheckStatus = casts_1.CheckStatus; 431 | /** 432 | * Access to {@link TransferStatus} enumeration, used in {@link Transfer} type, 433 | * {@link Client.getTransfers} and {@link Client.getTransfersPaginate} methods options 434 | */ 435 | Client.TransferStatus = casts_1.TransferStatus; 436 | exports.default = Client; 437 | -------------------------------------------------------------------------------- /lib/classes/ClientEmitter.d.ts: -------------------------------------------------------------------------------- 1 | import { ListenOptions } from 'net'; 2 | import { IncomingMessage } from 'http'; 3 | import { ServerOptions } from 'https'; 4 | import Client from './Client'; 5 | import { Invoice } from '../helpers/casts'; 6 | import { Middleware } from '../helpers/utils'; 7 | /** 8 | * Check webhook data signature 9 | * 10 | * @param apiKey - Api key 11 | * @param signature - Webhook request signature 12 | * @param body - Webhook request body 13 | * 14 | * @returns Checking result 15 | */ 16 | export declare const checkSignature: (apiKey: string, signature: string, body: any) => boolean; 17 | /** 18 | * Read and parsing to JSON request body 19 | * 20 | * @param req - Node.js built-in IncomingMessage object 21 | * 22 | * @returns Promise, what resolved to parsed body or `null` for parsing error 23 | */ 24 | export declare const readRequestBody: (req: IncomingMessage) => Promise; 25 | /** 26 | * Main class for work with API for Node.js 27 | * 28 | * Library for Node.js default export this class 29 | * 30 | * @category External 31 | */ 32 | declare class ClientEmitter extends Client { 33 | /** Api key */ 34 | private _apiKey; 35 | /** Handling webhooks created Node.js built-in server */ 36 | private _server; 37 | /** Event listeners store */ 38 | private _events; 39 | /** {@inheritDoc Client:constructor} */ 40 | constructor(apiKey: string, endpoint?: string); 41 | /** 42 | * Create handling webhooks server 43 | * 44 | * If you app work behind proxy and no need create HTTPS server, 45 | * no pass `key` and `cert` fields and add `http` field with `true` value: `{ http: true }` 46 | * 47 | * Note: if you want to use self-signed certificate 48 | * you must uploat it in CryptoBot API application settings 49 | * 50 | * @param serverOptions - Node.js built-in server options 51 | * @param secretPath - Webhooks secret path, processing webhooks takes place only on it 52 | * @param listenOptions - Node.js built-in server listen options 53 | * 54 | * @throws Error - If create server error 55 | * 56 | * @returns Promise, what resolved `void` 57 | */ 58 | createServer(serverOptions: ServerOptions & { 59 | http?: boolean; 60 | }, secretPath?: string, listenOptions?: ListenOptions): Promise; 61 | /** 62 | * Close created handling webhooks server 63 | * 64 | * @throws Error - If server not was started or closing error 65 | * 66 | * @returns Promise, what resolved `void` 67 | */ 68 | closeServer(): Promise; 69 | /** 70 | * Create middleware function for Express.js-like API 71 | * 72 | * @returns Middleware function 73 | */ 74 | middleware(): Middleware; 75 | /** 76 | * Subscribes to `paid` event 77 | * 78 | * See {@link ClientEmitter._emit} to more about event listener 79 | * 80 | * @param event - `paid` event name 81 | * @param listener - Event listener with `invoice` and `requestDate` callback parameters 82 | */ 83 | on(event: 'paid', listener: (invoice: Invoice, requestDate: Date) => any): void; 84 | /** 85 | * Unsubscribes from `paid` event 86 | * 87 | * @param event - `paid` event name 88 | * @param listener - Event listener with `invoice` and `requestDate` callback parameters 89 | */ 90 | off(event: 'paid', listener: (invoice: Invoice, requestDate: Date) => any): void; 91 | /** 92 | * Emit event to listeners 93 | * 94 | * @param event - `paid` event name 95 | * @param invoice - Paid invoice information object 96 | * @param requestDate - Date of occurrence of event, need to filter old event. 97 | * If server is not available, backend API try resend webhooks by timeout, 98 | * so when server becomes available again, many old events 99 | * will be sent from backend API. 100 | */ 101 | private _emit; 102 | /** 103 | * Handling webhook data, send response and emit events 104 | * 105 | * @param data - Parsed request body 106 | * @param req - Node.js built-in IncomingMessage object 107 | * @param res - Node.js built-in ServerResponse object 108 | */ 109 | private _handleWebhook; 110 | } 111 | export default ClientEmitter; 112 | -------------------------------------------------------------------------------- /lib/classes/ClientEmitter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.readRequestBody = exports.checkSignature = void 0; 4 | const crypto_1 = require("crypto"); 5 | const http_1 = require("http"); 6 | const https_1 = require("https"); 7 | const Client_1 = require("./Client"); 8 | const casts_1 = require("../helpers/casts"); 9 | /** 10 | * Check webhook data signature 11 | * 12 | * @param apiKey - Api key 13 | * @param signature - Webhook request signature 14 | * @param body - Webhook request body 15 | * 16 | * @returns Checking result 17 | */ 18 | const checkSignature = (apiKey, signature, body) => { 19 | try { 20 | const secret = (0, crypto_1.createHash)('sha256').update(apiKey).digest(); 21 | const checkString = JSON.stringify(body); 22 | const hmac = (0, crypto_1.createHmac)('sha256', secret).update(checkString).digest('hex'); 23 | return hmac === signature; 24 | } 25 | catch { 26 | return false; 27 | } 28 | }; 29 | exports.checkSignature = checkSignature; 30 | /** 31 | * Read and parsing to JSON request body 32 | * 33 | * @param req - Node.js built-in IncomingMessage object 34 | * 35 | * @returns Promise, what resolved to parsed body or `null` for parsing error 36 | */ 37 | const readRequestBody = (req) => new Promise((resolve) => { 38 | let body = ''; 39 | req.on('data', (chunk) => { 40 | body += chunk; 41 | }); 42 | req.on('end', () => { 43 | let data; 44 | try { 45 | data = JSON.parse(body); 46 | } 47 | catch { 48 | resolve(null); 49 | return; 50 | } 51 | resolve(data); 52 | }); 53 | }); 54 | exports.readRequestBody = readRequestBody; 55 | // Because `tsdoc` not support `@category` tag, but `typedoc` support 56 | /* eslint-disable tsdoc/syntax */ 57 | /** 58 | * Main class for work with API for Node.js 59 | * 60 | * Library for Node.js default export this class 61 | * 62 | * @category External 63 | */ 64 | /* eslint-enable tsdoc/syntax */ 65 | class ClientEmitter extends Client_1.default { 66 | // Because `tsdoc` throw `tsdoc-reference-selector-missing-parens`, 67 | // but `typedoc` doesn't recognize reference in parentheses 68 | /* eslint-disable tsdoc/syntax */ 69 | /** {@inheritDoc Client:constructor} */ 70 | /* eslint-enable tsdoc/syntax */ 71 | constructor(apiKey, endpoint = 'mainnet') { 72 | super(apiKey, endpoint); 73 | /** Event listeners store */ 74 | this._events = {}; 75 | this._apiKey = apiKey; 76 | } 77 | /** 78 | * Create handling webhooks server 79 | * 80 | * If you app work behind proxy and no need create HTTPS server, 81 | * no pass `key` and `cert` fields and add `http` field with `true` value: `{ http: true }` 82 | * 83 | * Note: if you want to use self-signed certificate 84 | * you must uploat it in CryptoBot API application settings 85 | * 86 | * @param serverOptions - Node.js built-in server options 87 | * @param secretPath - Webhooks secret path, processing webhooks takes place only on it 88 | * @param listenOptions - Node.js built-in server listen options 89 | * 90 | * @throws Error - If create server error 91 | * 92 | * @returns Promise, what resolved `void` 93 | */ 94 | createServer(serverOptions, secretPath = '/', listenOptions = { port: 443 }) { 95 | return new Promise((resolve, reject) => { 96 | const requestListener = (req, res) => { 97 | if (req.url !== secretPath) { 98 | res.statusCode = 404; 99 | res.end(); 100 | return; 101 | } 102 | (0, exports.readRequestBody)(req).then((data) => this._handleWebhook(data, req, res)); 103 | }; 104 | let server; 105 | try { 106 | server = serverOptions.http === true 107 | ? (0, http_1.createServer)(serverOptions, requestListener) 108 | : (0, https_1.createServer)(serverOptions, requestListener); 109 | } 110 | catch (err) { 111 | reject(err); 112 | return; 113 | } 114 | server.on('error', reject); 115 | try { 116 | server.listen(listenOptions, () => { 117 | this._server = server; 118 | resolve(); 119 | }); 120 | } 121 | catch (err) { 122 | reject(err); 123 | } 124 | }); 125 | } 126 | /** 127 | * Close created handling webhooks server 128 | * 129 | * @throws Error - If server not was started or closing error 130 | * 131 | * @returns Promise, what resolved `void` 132 | */ 133 | closeServer() { 134 | if (!this._server) 135 | return Promise.reject(new Error('Server not started')); 136 | return new Promise((resolve, reject) => { 137 | this._server.close((err) => { 138 | if (err) 139 | reject(err); 140 | else 141 | resolve(); 142 | }); 143 | }); 144 | } 145 | /** 146 | * Create middleware function for Express.js-like API 147 | * 148 | * @returns Middleware function 149 | */ 150 | middleware() { 151 | return (req, res) => { 152 | Promise.resolve() 153 | .then(() => req.body || (0, exports.readRequestBody)(req)) 154 | .then((data) => this._handleWebhook(data, req, res)); 155 | }; 156 | } 157 | /** 158 | * Subscribes to event 159 | * 160 | * @param event - Event name 161 | * @param listener - Event listener 162 | */ 163 | on(event, listener) { 164 | if (!this._events[event]) 165 | this._events[event] = []; 166 | this._events[event].push(listener); 167 | } 168 | /** 169 | * Unsubscribes from event 170 | * 171 | * @param event - Event name 172 | * @param listener - Event listener 173 | */ 174 | off(event, listener) { 175 | if (!this._events[event]) 176 | return; 177 | const idx = this._events[event].indexOf(listener); 178 | if (idx > -1) 179 | this._events[event].splice(idx, 1); 180 | } 181 | /** 182 | * Emit event to listeners 183 | * 184 | * @param event - Event name 185 | * @param params - Call event listeners parameters 186 | */ 187 | _emit(event, ...params) { 188 | if (!this._events[event]) 189 | return; 190 | this._events[event].forEach((listener) => { 191 | listener(...params); 192 | }); 193 | } 194 | /** 195 | * Handling webhook data, send response and emit events 196 | * 197 | * @param data - Parsed request body 198 | * @param req - Node.js built-in IncomingMessage object 199 | * @param res - Node.js built-in ServerResponse object 200 | */ 201 | _handleWebhook(data, req, res) { 202 | if (!data) { 203 | res.statusCode = 401; 204 | res.end(); 205 | return; 206 | } 207 | const header = req.headers['crypto-pay-api-signature']; 208 | const signature = Array.isArray(header) ? header[0] : header; 209 | if (!(0, exports.checkSignature)(this._apiKey, signature, data)) { 210 | res.statusCode = 401; 211 | res.end(); 212 | return; 213 | } 214 | if (data.update_type === 'invoice_paid') { 215 | this._emit('paid', (0, casts_1.toInvoice)(data.payload), new Date(data.request_date)); 216 | } 217 | res.end(); 218 | } 219 | } 220 | exports.default = ClientEmitter; 221 | -------------------------------------------------------------------------------- /lib/classes/Store.d.ts: -------------------------------------------------------------------------------- 1 | import { Currencies, ExchangeRates, Me } from '../helpers/casts'; 2 | import { ApiMethod } from '../helpers/utils'; 3 | import Transport from './Transport'; 4 | /** 5 | * Create cached fetch handler for passed data type 6 | * 7 | * Store using for data types, which change infrequently. 8 | * For example, app infomation from `getMe` method. 9 | * For method calls more often than the passed update period, 10 | * returned data from cache without real request to backend API 11 | * 12 | * @remarks 13 | * Returned update data function receive `isForce` boolean parameter, 14 | * if it `true`, for this method call function makes real request to backend API 15 | * 16 | * @typeParam T - One of library methods return data type 17 | * 18 | * @param transport - Transport class instance 19 | * @param method - Backend API method, data type related 20 | * @param castFn - Convert backend API result to inner library method result type function 21 | * @param updatePeriod - Updatin data from backend API period 22 | * 23 | * @returns Update data type function 24 | */ 25 | export declare const createFetchHandler: (transport: Transport, method: ApiMethod, castFn: (value: any) => T, updatePeriod: number) => (isForce?: boolean) => Promise; 26 | /** 27 | * Wrapper for API methods that return possible cached data 28 | * 29 | * @category External 30 | */ 31 | export default class Store { 32 | /** Update period for fetching currencies from backend API in seconds */ 33 | private static _CURRENCIES_UPDATE_PERIOD; 34 | /** Update period for fetching exhange rates from backend API in seconds */ 35 | private static _EXCHANGE_RATES_UPDATE_PERIOD; 36 | /** Update period for fetching app infomation from backend API in seconds */ 37 | private static _ME_UPDATE_PERIOD; 38 | /** Transport class instance */ 39 | protected _transport: Transport; 40 | /** 41 | * {@link Store.getCurrencies} method fetch data handler, 42 | * see {@link createFetchHandler} for more 43 | */ 44 | private _currenciesFetchHandler; 45 | /** 46 | * {@link Store.getExchangeRates} method fetch data handler, 47 | * see {@link createFetchHandler} for more 48 | */ 49 | private _exchangeRatesFetchHandler; 50 | /** 51 | * {@link Store.getMe} method fetch data handler, 52 | * see {@link createFetchHandler} for more 53 | */ 54 | private _meFetchHandler; 55 | /** 56 | * Create class instance 57 | * 58 | * @param apiKey - Crypto Bot API key, looks like '1234:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' 59 | * @param endpoint - API endpoint url or 'mainnet' or 'testnet' 60 | * for hardcoded in library endpoint urls 61 | * 62 | * @throws Error - If passed invalid API key or endpoint 63 | */ 64 | constructor(apiKey: string, endpoint?: 'mainnet' | 'testnet' | string); 65 | /** 66 | * Get API supported currencies infomation 67 | * 68 | * Use {@link toCurrencies} backend API result convert function 69 | * 70 | * @param isForce - If true, return fresh data from backend API, not from cache 71 | * 72 | * @throws Error - If there is an error sending request to backend API or parsing response 73 | * 74 | * @returns Promise, what resolved to API supported currencies infomation object 75 | */ 76 | getCurrencies(isForce?: boolean): Promise; 77 | /** 78 | * Get API supported currencies exchange rate infomation 79 | * 80 | * Use {@link toExchangeRates} backend API result convert function 81 | * 82 | * @param isForce - If true, return fresh data from backend API, not from cache 83 | * 84 | * @throws Error - If there is an error sending request to backend API or parsing response 85 | * 86 | * @returns Promise, what resolved to API supported currencies exchange rate infomation object 87 | */ 88 | getExchangeRates(isForce?: boolean): Promise; 89 | /** 90 | * Get associated with passed API key app infomation 91 | * 92 | * Use {@link toMe} backend API result convert function 93 | * 94 | * @param isForce - If true, return fresh data from backend API, not from cache 95 | * 96 | * @throws Error - If there is an error sending request to backend API or parsing response 97 | * 98 | * @returns Promise, what resolved to associated with passed API key app infomation object 99 | */ 100 | getMe(isForce?: boolean): Promise; 101 | } 102 | -------------------------------------------------------------------------------- /lib/classes/Store.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.createFetchHandler = void 0; 4 | const casts_1 = require("../helpers/casts"); 5 | const Transport_1 = require("./Transport"); 6 | /** 7 | * Create cached fetch handler for passed data type 8 | * 9 | * Store using for data types, which change infrequently. 10 | * For example, app infomation from `getMe` method. 11 | * For method calls more often than the passed update period, 12 | * returned data from cache without real request to backend API 13 | * 14 | * @remarks 15 | * Returned update data function receive `isForce` boolean parameter, 16 | * if it `true`, for this method call function makes real request to backend API 17 | * 18 | * @typeParam T - One of library methods return data type 19 | * 20 | * @param transport - Transport class instance 21 | * @param method - Backend API method, data type related 22 | * @param castFn - Convert backend API result to inner library method result type function 23 | * @param updatePeriod - Updatin data from backend API period 24 | * 25 | * @returns Update data type function 26 | */ 27 | const createFetchHandler = (transport, method, castFn, updatePeriod) => { 28 | let promise = null; 29 | let prevUpdateStamp = 0; 30 | let data; 31 | return (isForce = false) => { 32 | // If data fetching in process, return same promise 33 | // Need to prevent same multiple requests in one time 34 | // if Client class methods call parallel 35 | // This situation may arise due to the fact that some 36 | // methods need multiple backend API response to 37 | // return prepared result. For example, Client.getBalances 38 | // method need currencies information from getCurrencies 39 | // backend API method for correct formatting of amounts 40 | if (promise) 41 | return promise; 42 | // Calculate current update perion number 43 | const updateStamp = Math.floor(+(new Date()) / 1000 / updatePeriod); 44 | if (updateStamp === prevUpdateStamp && !isForce) { 45 | return Promise.resolve(data); 46 | } 47 | prevUpdateStamp = updateStamp; 48 | promise = transport.call(method).then((value) => { 49 | data = castFn(value); 50 | promise = null; 51 | return data; 52 | }); 53 | return promise; 54 | }; 55 | }; 56 | exports.createFetchHandler = createFetchHandler; 57 | // Because `tsdoc` not support `@category` tag, but `typedoc` support 58 | /* eslint-disable tsdoc/syntax */ 59 | /** 60 | * Wrapper for API methods that return possible cached data 61 | * 62 | * @category External 63 | */ 64 | /* eslint-enable tsdoc/syntax */ 65 | class Store { 66 | /** 67 | * Create class instance 68 | * 69 | * @param apiKey - Crypto Bot API key, looks like '1234:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' 70 | * @param endpoint - API endpoint url or 'mainnet' or 'testnet' 71 | * for hardcoded in library endpoint urls 72 | * 73 | * @throws Error - If passed invalid API key or endpoint 74 | */ 75 | constructor(apiKey, endpoint = 'mainnet') { 76 | this._transport = new Transport_1.default(apiKey, endpoint); 77 | } 78 | /** 79 | * Get API supported currencies infomation 80 | * 81 | * Use {@link toCurrencies} backend API result convert function 82 | * 83 | * @param isForce - If true, return fresh data from backend API, not from cache 84 | * 85 | * @throws Error - If there is an error sending request to backend API or parsing response 86 | * 87 | * @returns Promise, what resolved to API supported currencies infomation object 88 | */ 89 | getCurrencies(isForce) { 90 | if (!this._currenciesFetchHandler) { 91 | this._currenciesFetchHandler = (0, exports.createFetchHandler)(this._transport, 'getCurrencies', casts_1.toCurrencies, Store._CURRENCIES_UPDATE_PERIOD); 92 | } 93 | return this._currenciesFetchHandler(isForce); 94 | } 95 | /** 96 | * Get API supported currencies exchange rate infomation 97 | * 98 | * Use {@link toExchangeRates} backend API result convert function 99 | * 100 | * @param isForce - If true, return fresh data from backend API, not from cache 101 | * 102 | * @throws Error - If there is an error sending request to backend API or parsing response 103 | * 104 | * @returns Promise, what resolved to API supported currencies exchange rate infomation object 105 | */ 106 | getExchangeRates(isForce) { 107 | if (!this._exchangeRatesFetchHandler) { 108 | this._exchangeRatesFetchHandler = (0, exports.createFetchHandler)(this._transport, 'getExchangeRates', casts_1.toExchangeRates, Store._EXCHANGE_RATES_UPDATE_PERIOD); 109 | } 110 | return this._exchangeRatesFetchHandler(isForce); 111 | } 112 | /** 113 | * Get associated with passed API key app infomation 114 | * 115 | * Use {@link toMe} backend API result convert function 116 | * 117 | * @param isForce - If true, return fresh data from backend API, not from cache 118 | * 119 | * @throws Error - If there is an error sending request to backend API or parsing response 120 | * 121 | * @returns Promise, what resolved to associated with passed API key app infomation object 122 | */ 123 | getMe(isForce = false) { 124 | if (!this._meFetchHandler) { 125 | this._meFetchHandler = (0, exports.createFetchHandler)(this._transport, 'getMe', casts_1.toMe, Store._ME_UPDATE_PERIOD); 126 | } 127 | return this._meFetchHandler(isForce); 128 | } 129 | } 130 | /** Update period for fetching currencies from backend API in seconds */ 131 | Store._CURRENCIES_UPDATE_PERIOD = 3600; 132 | /** Update period for fetching exhange rates from backend API in seconds */ 133 | Store._EXCHANGE_RATES_UPDATE_PERIOD = 60; 134 | /** Update period for fetching app infomation from backend API in seconds */ 135 | Store._ME_UPDATE_PERIOD = 3600; 136 | exports.default = Store; 137 | -------------------------------------------------------------------------------- /lib/classes/Transport.d.ts: -------------------------------------------------------------------------------- 1 | import { ApiMethod } from '../helpers/utils'; 2 | /** 3 | * Make backend API calls 4 | */ 5 | export default class Transport { 6 | /** RegExp to check API key */ 7 | static KEY_CHECK_REGEXP: RegExp; 8 | /** Api key */ 9 | private _apiKey; 10 | /** Backend API endpoint base url */ 11 | private _baseUrl; 12 | /** 13 | * Create class instance 14 | * 15 | * @param apiKey - Crypto Bot API key, looks like '1234:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' 16 | * @param endpoint - API endpoint url or 'mainnet' or 'testnet' 17 | * for hardcoded in library endpoint urls 18 | * 19 | * @throws Error - If passed invalid API key or endpoint 20 | */ 21 | constructor(apiKey: string, endpoint: 'mainnet' | 'testnet' | string); 22 | /** 23 | * Make request to backend API, handle errors and return result 24 | * 25 | * @param method - Backend API method name 26 | * @param parameters - Method parameters object 27 | * 28 | * @throws Error - If response have errors 29 | * 30 | * @returns Promise, what resolved to API response `result` field 31 | */ 32 | call(method: ApiMethod, parameters?: object): Promise; 33 | } 34 | -------------------------------------------------------------------------------- /lib/classes/Transport.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const utils_1 = require("../helpers/utils"); 4 | const http_1 = require("../request/http"); 5 | /** 6 | * Make backend API calls 7 | */ 8 | class Transport { 9 | /** 10 | * Create class instance 11 | * 12 | * @param apiKey - Crypto Bot API key, looks like '1234:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' 13 | * @param endpoint - API endpoint url or 'mainnet' or 'testnet' 14 | * for hardcoded in library endpoint urls 15 | * 16 | * @throws Error - If passed invalid API key or endpoint 17 | */ 18 | constructor(apiKey, endpoint) { 19 | if (!Transport.KEY_CHECK_REGEXP.test(apiKey)) { 20 | throw new Error('API key looks like invalid'); 21 | } 22 | let url; 23 | if (endpoint === 'mainnet') { 24 | url = 'https://pay.crypt.bot/api'; 25 | } 26 | else if (endpoint === 'testnet') { 27 | url = 'https://testnet-pay.crypt.bot/api'; 28 | } 29 | else if (!(0, utils_1.isValidUrl)(endpoint)) { 30 | throw new Error('Endpoint parameter not contain valid URL'); 31 | } 32 | else { 33 | url = endpoint; 34 | } 35 | this._apiKey = apiKey; 36 | this._baseUrl = `${url}/`; 37 | } 38 | /** 39 | * Make request to backend API, handle errors and return result 40 | * 41 | * @param method - Backend API method name 42 | * @param parameters - Method parameters object 43 | * 44 | * @throws Error - If response have errors 45 | * 46 | * @returns Promise, what resolved to API response `result` field 47 | */ 48 | call(method, parameters) { 49 | // Format url query part from passed parameters object 50 | let qs = ''; 51 | if (parameters) { 52 | Object.keys(parameters).forEach((name) => { 53 | let value = parameters[name]; 54 | if (Array.isArray(value)) 55 | value = value.join(','); 56 | else 57 | value = value.toString(); 58 | qs += `&${name}=${encodeURIComponent(value)}`; 59 | }); 60 | } 61 | return (0, http_1.default)(this._baseUrl + method + (qs.length ? `?${qs.substr(1)}` : ''), this._apiKey) 62 | .then((rawResponse) => { 63 | let response; 64 | try { 65 | response = JSON.parse(rawResponse); 66 | } 67 | catch { 68 | throw new Error(`Response parse error, raw reponse:\n${rawResponse}`); 69 | } 70 | if (response.ok !== true) { 71 | if (!response.error) 72 | throw new Error('Api response unknown error'); 73 | throw new Error(`Api response error ${JSON.stringify(response.error)}`); 74 | } 75 | return response.result; 76 | }); 77 | } 78 | } 79 | /** RegExp to check API key */ 80 | Transport.KEY_CHECK_REGEXP = /\d{1,}:[a-zA-Z0-9]{35}/; 81 | exports.default = Transport; 82 | -------------------------------------------------------------------------------- /lib/helpers/casts.d.ts: -------------------------------------------------------------------------------- 1 | import { PaidBtnName } from './utils'; 2 | /** Result type value for {@link Client.getBalances} method */ 3 | export type Balance = { 4 | available: string; 5 | onhold: string; 6 | }; 7 | /** Result type for {@link Client.getBalances} method */ 8 | export type Balances = { 9 | [variant in CryptoCurrencyCode]: Balance; 10 | }; 11 | /** Result type for {@link Client.getBalances} method */ 12 | export type BalancesType = { 13 | [variant in CryptoCurrencyCode]: string; 14 | }; 15 | export type CryptoCurrencyCode = 'USDT' | 'TON' | 'GRAM' | 'NOT' | 'MY' | 'DOGS' | 'BTC' | 'LTC' | 'ETH' | 'BNB' | 'TRX' | 'WIF' | 'USDC' | 'TRUMP' | 'MELANIA' | 'SOL' | 'DOGE' | 'PEPE' | 'BONK' | 'MAJOR' | 'HMSTR' | 'CATI' | 'MEMHASH'; 16 | export type FiatCurrencyCode = 'USD' | 'EUR' | 'RUB' | 'BYN' | 'UAH' | 'GBP' | 'CNY' | 'KZT' | 'UZS' | 'GEL' | 'TRY' | 'AMD' | 'THB' | 'INR' | 'BRL' | 'IDR' | 'AZN' | 'AED' | 'PLN' | 'ILS' | 'KGS' | 'TJS'; 17 | export type CurrencyCode = CryptoCurrencyCode | FiatCurrencyCode; 18 | /** Possible currency types */ 19 | export declare enum CurrencyType { 20 | Crypto = "crypto", 21 | Fiat = "fiat", 22 | Unknown = "unknown" 23 | } 24 | /** Possible detailed currency types */ 25 | export declare enum DetailedCurrencyType { 26 | Blockchain = "blockchain", 27 | Stablecoin = "stablecoin", 28 | Fiat = "fiat", 29 | Unknown = "unknown" 30 | } 31 | /** Possible invoice statuses */ 32 | export declare enum InvoiceStatus { 33 | Active = "active", 34 | Paid = "paid", 35 | Expired = "expired", 36 | Unknown = "unknown" 37 | } 38 | /** Possible check statuses */ 39 | export declare enum CheckStatus { 40 | Active = "active", 41 | Activated = "activated", 42 | Unknown = "unknown" 43 | } 44 | /** Possible transfer statuses */ 45 | export declare enum TransferStatus { 46 | Completed = "completed", 47 | Unknown = "unknown" 48 | } 49 | /** 50 | * Currency type object for {@link Store.getCurrencies} 51 | * and {@link Client.getCurrency} methods results 52 | */ 53 | export type Currency = { 54 | /** Currency code */ 55 | code: CurrencyCode; 56 | /** Currency name */ 57 | name: string; 58 | /** Crypto currency office website url */ 59 | url?: string; 60 | /** Currency decimals count */ 61 | decimals: number; 62 | /** Currency type */ 63 | type: DetailedCurrencyType; 64 | }; 65 | /** Result type for {@link Store.getCurrencies} method */ 66 | export type Currencies = { 67 | [variant in CurrencyCode]?: Currency; 68 | }; 69 | /** 70 | * Exchange rate type object for {@link Store.getExchangeRates} 71 | * and {@link Client.getExchangeRate} methods results 72 | */ 73 | export type ExchangeRate = { 74 | /** Source currency code */ 75 | source: CurrencyCode; 76 | /** Target currency code */ 77 | target: CurrencyCode; 78 | /** Source to target exchange rate */ 79 | rate: string; 80 | /** True, if the received rate is up-to-date */ 81 | isValid: boolean; 82 | }; 83 | /** Result type for {@link Store.getExchangeRates} method */ 84 | export type ExchangeRates = ExchangeRate[]; 85 | /** 86 | * Transfer type object for {@link Client.getTransfers} and {@link Client.transfer} methods results 87 | */ 88 | export type Transfer = { 89 | /** Transfer identifier */ 90 | id: number; 91 | /** 92 | * Transfer spend identifier, optional because not returned from `transfer` API method call 93 | */ 94 | spendId?: string; 95 | /** Telegram user ID the transfer was sent to */ 96 | userId: number; 97 | /** Transfer asset */ 98 | asset: CryptoCurrencyCode; 99 | /** Transfer amount */ 100 | amount: string; 101 | /** Transfer status */ 102 | status: TransferStatus; 103 | /** Transfer completed date */ 104 | completedAt: Date; 105 | /** Check activated date */ 106 | comment?: string; 107 | }; 108 | /** 109 | * Check type object for {@link Client.getChecks}, {@link Client.getChecksPaginate} 110 | * and {@link Client.createCheck} methods results 111 | */ 112 | export type Check = { 113 | /** Check identifier */ 114 | id: number; 115 | /** Check hash */ 116 | hash: string; 117 | /** Check asset */ 118 | asset: CryptoCurrencyCode; 119 | /** Check amount */ 120 | amount: string; 121 | /** Check receive url for user by bot */ 122 | botCheckUrl: string; 123 | /** Check status */ 124 | status: CheckStatus; 125 | /** Check created date */ 126 | createdAt: Date; 127 | /** Check activated date */ 128 | activatedAt?: Date; 129 | /** 130 | * ID of the user who will be able to activate the check, 131 | * only if passed in check creation, 132 | * if exists, field `pinToUsername` will be absent 133 | */ 134 | pinToUserId?: number; 135 | /** 136 | * A user with the specified username will be able to activate the check, 137 | * only if passed in check creation, 138 | * if exists, field `pinToUserId` will be absent 139 | */ 140 | pinToUsername?: string; 141 | }; 142 | /** 143 | * Invoice type object for {@link Client.getInvoices}, {@link Client.getInvoicesPaginate}, 144 | * {@link Client.createInvoice} methods results and {@link ClientEmitter} `paid` event emit 145 | */ 146 | export type Invoice = { 147 | /** Invoice identifier */ 148 | id: number; 149 | /** Invoice status */ 150 | status: InvoiceStatus; 151 | /** Invoice hash */ 152 | hash: string; 153 | /** Invoice currency type */ 154 | currencyType: CurrencyType; 155 | /** Invoice currency code */ 156 | currency: CurrencyCode; 157 | /** Invoice amount */ 158 | amount: string; 159 | /** Invoice pay url for user by bot */ 160 | botPayUrl: string; 161 | /** Invoice pay url for user by mini app */ 162 | miniAppPayUrl: string; 163 | /** Invoice pay url for user by web app */ 164 | webAppPayUrl: string; 165 | /** Is invoice allow user comment */ 166 | isAllowComments: boolean; 167 | /** Is user can pay invoice anonymously */ 168 | isAllowAnonymous: boolean; 169 | /** Invoice created date */ 170 | createdAt: Date; 171 | /** Text of the hidden message, only if set in invoice creation */ 172 | hiddenMessage?: string; 173 | /** Is invoice paid anonymously, only for paid invoice */ 174 | isPaidAnonymously?: boolean; 175 | /** Invoice paid date, only for paid invoice */ 176 | paidAt?: Date; 177 | /** Expiration date, only if set pay limit time in invoice creation */ 178 | expirationDate?: Date; 179 | /** Invoice displayed to user description, only if `description` passed in invoice creation */ 180 | description?: string; 181 | /** 182 | * Invoice visible only to app payload, only if `payload` passed in invoice creation 183 | * 184 | * If for invoice creation passed not string in this field, will be converted by JSON.parse 185 | */ 186 | payload?: any; 187 | /** 188 | * Invoice left user comment, only if set `isAllowComments` to true in invoice creation 189 | * and user left comment 190 | */ 191 | comment?: string; 192 | /** 193 | * Invoice displayed to user paid button name, 194 | * only if `paidBtnName` passed in invoice creation 195 | */ 196 | paidBtnName?: PaidBtnName; 197 | /** 198 | * Invoice displayed to user paid button url, 199 | * only if `paidBtnUrl` passed in invoice creation 200 | */ 201 | paidBtnUrl?: string; 202 | /** 203 | * Asset of service fees charged when the invoice was paid, only if status is InvoiceStatus.Paid 204 | */ 205 | feeAsset?: CryptoCurrencyCode; 206 | /** 207 | * Amount of service fees charged when the invoice was paid, only if status is InvoiceStatus.Paid 208 | */ 209 | fee?: number; 210 | /** 211 | * Price of the asset in USD, only if status is InvoiceStatus.Paid 212 | */ 213 | usdRate?: number; 214 | /** 215 | * List of assets which can be used to pay the invoice, only if set in invoice creation 216 | */ 217 | acceptedAssets?: CryptoCurrencyCode[]; 218 | /** 219 | * Cryptocurrency alphabetic code for which the invoice was paid, 220 | * only if currency type is CurrencyType.Fiat and status is InvoiceStatus.Paid 221 | */ 222 | paidAsset?: CryptoCurrencyCode; 223 | /** 224 | * Amount of the invoice for which the invoice was paid, 225 | * only if currency type is CurrencyType.Fiat and status is InvoiceStatus.Paid 226 | */ 227 | paidAmount?: number; 228 | /** 229 | * The rate of the paid_asset valued in the fiat currency, 230 | * only if currency type is CurrencyType.Fiat and status is InvoiceStatus.Paid 231 | */ 232 | paidFiatRate?: number; 233 | }; 234 | /** Result type object for {@link Client.getStats} method */ 235 | export type Stats = { 236 | /** Total volume of paid invoices in USD */ 237 | volume: string; 238 | /** Conversion of all created invoices */ 239 | conversion: string; 240 | /** The unique number of users who have paid the invoice */ 241 | uniqueUsersCount: number; 242 | /** Total created invoice count */ 243 | createdInvoiceCount: number; 244 | /** Total paid invoice count */ 245 | paidInvoiceCount: number; 246 | /** The date on which the statistics calculation was started */ 247 | startAt: Date; 248 | /** The date on which the statistics calculation was ended */ 249 | endAt: Date; 250 | }; 251 | /** Result type object for {@link Store.getMe} method */ 252 | export type Me = { 253 | /** App identifier */ 254 | id: number; 255 | /** App name */ 256 | name: string; 257 | /** Using Telegram bot username */ 258 | bot: string; 259 | }; 260 | /** 261 | * Convert backend API result to library result object to return in 262 | * {@link Client.getBalances} method 263 | * 264 | * @param input - Backend API result 265 | * 266 | * @throws Error - If input parameter is not array 267 | * 268 | * @returns Converted result 269 | */ 270 | export declare const toBalances: (input: any) => Balances; 271 | /** 272 | * Convert backend API result to library result object to return in 273 | * {@link Store.getCurrencies} method 274 | * 275 | * @param input - Backend API result 276 | * 277 | * @returns Converted result 278 | */ 279 | export declare const toCurrencies: (input: any) => Currencies; 280 | /** 281 | * Convert backend API result to library result object to return in 282 | * {@link Store.getExchangeRates} method result 283 | * 284 | * @param input - Backend API result 285 | * 286 | * @returns Converted result 287 | */ 288 | export declare const toExchangeRates: (input: any) => ExchangeRates; 289 | /** 290 | * Convert backend API result to library result object to return in 291 | * {@link Client.createInvoice} method, {@link toInvoices} function 292 | * and {@link ClientEmitter} `paid` event emit 293 | * 294 | * @param input - Backend API result 295 | * 296 | * @returns Converted result 297 | */ 298 | export declare const toInvoice: (input: any) => Invoice; 299 | /** 300 | * Convert backend API result to library result object to return in 301 | * {@link Client.createCheck} method and {@link toChecks} function 302 | * 303 | * @param input - Backend API result 304 | * 305 | * @returns Converted result 306 | */ 307 | export declare const toCheck: (input: any) => Check; 308 | /** 309 | * Convert backend API result to library result object to return in 310 | * {@link Client.transfer} method and {@link toTransfers} function 311 | * 312 | * @param input - Backend API result 313 | * 314 | * @returns Converted result 315 | */ 316 | export declare const toTransfer: (input: any) => Transfer; 317 | /** 318 | * Convert backend API result to library result object to return in 319 | * {@link Client.getInvoices} and {@link Client.getInvoicesPaginate} 320 | * methods 321 | * 322 | * @param input - Backend API result 323 | * 324 | * @returns Converted result 325 | */ 326 | export declare const toInvoices: (input: any) => Invoice[]; 327 | /** 328 | * Convert backend API result to library result object to return in 329 | * {@link Client.getChecks} and {@link Client.getChecksPaginate} 330 | * methods 331 | * 332 | * @param input - Backend API result 333 | * 334 | * @returns Converted result 335 | */ 336 | export declare const toChecks: (input: any) => Check[]; 337 | /** 338 | * Convert backend API result to library result object to return in 339 | * {@link Client.getTransfers} and {@link Client.getTransfersPaginate} 340 | * methods 341 | * 342 | * @param input - Backend API result 343 | * 344 | * @returns Converted result 345 | */ 346 | export declare const toTransfers: (input: any) => Transfer[]; 347 | /** 348 | * Convert backend API result to library result object to return in 349 | * {@link Client.getStats} method 350 | * 351 | * @param input - Backend API result 352 | * 353 | * @returns Converted result 354 | */ 355 | export declare const toStats: (input: any) => Stats; 356 | /** 357 | * Convert backend API result to library result object to return in 358 | * {@link Store.getMe} method 359 | * 360 | * @param input - Backend API result 361 | * 362 | * @returns Converted result 363 | */ 364 | export declare const toMe: (input: any) => Me; 365 | -------------------------------------------------------------------------------- /lib/helpers/casts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.toMe = exports.toStats = exports.toTransfers = exports.toChecks = exports.toInvoices = exports.toTransfer = exports.toCheck = exports.toInvoice = exports.toExchangeRates = exports.toCurrencies = exports.toBalances = exports.TransferStatus = exports.CheckStatus = exports.InvoiceStatus = exports.DetailedCurrencyType = exports.CurrencyType = void 0; 4 | /** Possible currency types */ 5 | var CurrencyType; 6 | (function (CurrencyType) { 7 | CurrencyType["Crypto"] = "crypto"; 8 | CurrencyType["Fiat"] = "fiat"; 9 | CurrencyType["Unknown"] = "unknown"; 10 | })(CurrencyType || (exports.CurrencyType = CurrencyType = {})); 11 | /** Possible detailed currency types */ 12 | var DetailedCurrencyType; 13 | (function (DetailedCurrencyType) { 14 | DetailedCurrencyType["Blockchain"] = "blockchain"; 15 | DetailedCurrencyType["Stablecoin"] = "stablecoin"; 16 | DetailedCurrencyType["Fiat"] = "fiat"; 17 | DetailedCurrencyType["Unknown"] = "unknown"; 18 | })(DetailedCurrencyType || (exports.DetailedCurrencyType = DetailedCurrencyType = {})); 19 | /** Possible invoice statuses */ 20 | var InvoiceStatus; 21 | (function (InvoiceStatus) { 22 | InvoiceStatus["Active"] = "active"; 23 | InvoiceStatus["Paid"] = "paid"; 24 | InvoiceStatus["Expired"] = "expired"; 25 | InvoiceStatus["Unknown"] = "unknown"; 26 | })(InvoiceStatus || (exports.InvoiceStatus = InvoiceStatus = {})); 27 | /** Possible check statuses */ 28 | var CheckStatus; 29 | (function (CheckStatus) { 30 | CheckStatus["Active"] = "active"; 31 | CheckStatus["Activated"] = "activated"; 32 | CheckStatus["Unknown"] = "unknown"; 33 | })(CheckStatus || (exports.CheckStatus = CheckStatus = {})); 34 | /** Possible transfer statuses */ 35 | var TransferStatus; 36 | (function (TransferStatus) { 37 | TransferStatus["Completed"] = "completed"; 38 | TransferStatus["Unknown"] = "unknown"; 39 | })(TransferStatus || (exports.TransferStatus = TransferStatus = {})); 40 | /** 41 | * Convert backend API result to library result object to return in 42 | * {@link Client.getBalances} method 43 | * 44 | * @param input - Backend API result 45 | * 46 | * @throws Error - If input parameter is not array 47 | * 48 | * @returns Converted result 49 | */ 50 | const toBalances = (input) => { 51 | if (!Array.isArray(input)) 52 | throw new Error(`Input is not array: ${JSON.stringify(input)}`); 53 | // Conver array to HashMap structure 54 | return input.reduce((accumulator, value) => { 55 | accumulator[value.currency_code] = { available: value.available, onhold: value.onhold }; 56 | return accumulator; 57 | }, {}); 58 | }; 59 | exports.toBalances = toBalances; 60 | /** 61 | * Convert backend API result to library result object to return in 62 | * {@link Store.getCurrencies} method 63 | * 64 | * @param input - Backend API result 65 | * 66 | * @returns Converted result 67 | */ 68 | const toCurrencies = (input) => { 69 | if (!Array.isArray(input)) 70 | return {}; 71 | return input.reduce((accumulator, value) => { 72 | if (value.code) { 73 | const code = value.code.toString(); 74 | let type = DetailedCurrencyType.Unknown; 75 | if (value.is_blockchain) 76 | type = DetailedCurrencyType.Blockchain; 77 | if (value.is_fiat) 78 | type = DetailedCurrencyType.Fiat; 79 | if (value.is_stablecoin) 80 | type = DetailedCurrencyType.Stablecoin; 81 | const currency = { 82 | code: code, 83 | name: value.name || '', 84 | decimals: value.decimals || 0, 85 | type, 86 | }; 87 | if (Object.prototype.hasOwnProperty.call(value, 'url')) 88 | currency.url = value.url; 89 | accumulator[code] = currency; 90 | } 91 | return accumulator; 92 | }, {}); 93 | }; 94 | exports.toCurrencies = toCurrencies; 95 | /** 96 | * Convert backend API result to library result object to return in 97 | * {@link Store.getExchangeRates} method result 98 | * 99 | * @param input - Backend API result 100 | * 101 | * @returns Converted result 102 | */ 103 | const toExchangeRates = (input) => { 104 | if (!Array.isArray(input)) 105 | return []; 106 | return input.map((value) => ({ 107 | source: value.source || '', 108 | target: value.target || '', 109 | rate: value.rate, 110 | isValid: value.is_valid, 111 | })); 112 | }; 113 | exports.toExchangeRates = toExchangeRates; 114 | /** 115 | * Convert backend API result to library result object to return in 116 | * {@link Client.createInvoice} method, {@link toInvoices} function 117 | * and {@link ClientEmitter} `paid` event emit 118 | * 119 | * @param input - Backend API result 120 | * 121 | * @returns Converted result 122 | */ 123 | const toInvoice = (input) => { 124 | const invoice = { 125 | id: input.invoice_id || 0, 126 | status: input.status || InvoiceStatus.Unknown, 127 | hash: input.hash || '', 128 | currencyType: input.currency_type || '', 129 | currency: input.asset || input.fiat || '', 130 | amount: input.amount || '0', 131 | isAllowComments: input.allow_comments || false, 132 | isAllowAnonymous: input.allow_anonymous || false, 133 | createdAt: new Date(input.created_at), 134 | botPayUrl: input.bot_invoice_url || '', 135 | miniAppPayUrl: input.mini_app_invoice_url || '', 136 | webAppPayUrl: input.web_app_invoice_url || '', 137 | }; 138 | if (invoice.currencyType === CurrencyType.Crypto) { 139 | invoice.currency = input.asset || ''; 140 | } 141 | if (invoice.currencyType === CurrencyType.Fiat) { 142 | invoice.currency = input.fiat || ''; 143 | } 144 | if (input.hidden_message !== undefined) 145 | invoice.hiddenMessage = input.hidden_message; 146 | if (input.paid_anonymously !== undefined) 147 | invoice.isPaidAnonymously = input.paid_anonymously; 148 | if (input.expiration_date !== undefined) 149 | invoice.expirationDate = new Date(input.expiration_date); 150 | if (input.paid_at !== undefined) 151 | invoice.paidAt = new Date(input.paid_at); 152 | if (input.description !== undefined) 153 | invoice.description = input.description; 154 | if (input.paid_btn_name !== undefined) 155 | invoice.paidBtnName = input.paid_btn_name; 156 | if (input.paid_btn_url !== undefined) 157 | invoice.paidBtnUrl = input.paid_btn_url; 158 | if (input.comment !== undefined) 159 | invoice.comment = input.comment; 160 | if (input.paid_usd_rate !== undefined) 161 | invoice.usdRate = parseFloat(input.paid_usd_rate) || 0; 162 | if (input.fee_asset !== undefined) 163 | invoice.feeAsset = input.fee_asset || ''; 164 | if (input.fee_amount !== undefined) 165 | invoice.fee = input.fee_amount || 0; 166 | if (input.accepted_assets !== undefined) 167 | invoice.acceptedAssets = input.accepted_assets; 168 | if (input.paid_asset !== undefined) 169 | invoice.paidAsset = input.paid_asset || ''; 170 | if (input.paid_amount !== undefined) 171 | invoice.paidAmount = parseFloat(input.paid_amount) || 0; 172 | if (input.paid_fiat_rate !== undefined) 173 | invoice.paidFiatRate = parseFloat(input.paid_fiat_rate) || 0; 174 | if (input.payload !== undefined) { 175 | let payload; 176 | try { 177 | payload = JSON.parse(input.payload); 178 | } 179 | catch { 180 | payload = input.payload; 181 | } 182 | invoice.payload = payload; 183 | } 184 | return invoice; 185 | }; 186 | exports.toInvoice = toInvoice; 187 | /** 188 | * Convert backend API result to library result object to return in 189 | * {@link Client.createCheck} method and {@link toChecks} function 190 | * 191 | * @param input - Backend API result 192 | * 193 | * @returns Converted result 194 | */ 195 | const toCheck = (input) => { 196 | const check = { 197 | id: input.check_id || 0, 198 | hash: input.hash || '', 199 | asset: input.asset || '', 200 | amount: input.amount || '0', 201 | botCheckUrl: input.bot_check_url || '', 202 | status: input.status || CheckStatus.Unknown, 203 | createdAt: new Date(input.created_at), 204 | }; 205 | if (input.activated_at !== undefined) 206 | check.activatedAt = new Date(input.activated_at); 207 | if (input.pin_to_user !== undefined && input.pin_to_user.pin_by !== undefined) { 208 | if (input.pin_to_user.pin_by === 'id' && input.pin_to_user.user_id !== undefined) { 209 | check.pinToUserId = input.pin_to_user.user_id; 210 | } 211 | if (input.pin_to_user.pin_by === 'username' && input.pin_to_user.username !== undefined) { 212 | check.pinToUsername = input.pin_to_user.username; 213 | } 214 | } 215 | return check; 216 | }; 217 | exports.toCheck = toCheck; 218 | /** 219 | * Convert backend API result to library result object to return in 220 | * {@link Client.transfer} method and {@link toTransfers} function 221 | * 222 | * @param input - Backend API result 223 | * 224 | * @returns Converted result 225 | */ 226 | const toTransfer = (input) => { 227 | const transfer = { 228 | id: input.transfer_id || 0, 229 | userId: input.user_id || 0, 230 | asset: input.asset || '', 231 | amount: input.amount || '0', 232 | status: input.status || TransferStatus.Unknown, 233 | completedAt: new Date(input.completed_at), 234 | }; 235 | if (input.spend_id !== undefined) 236 | transfer.spendId = input.spend_id; 237 | if (input.comment !== undefined) 238 | transfer.comment = input.comment; 239 | return transfer; 240 | }; 241 | exports.toTransfer = toTransfer; 242 | /** 243 | * Convert backend API result to library result object to return in 244 | * {@link Client.getInvoices} and {@link Client.getInvoicesPaginate} 245 | * methods 246 | * 247 | * @param input - Backend API result 248 | * 249 | * @returns Converted result 250 | */ 251 | const toInvoices = (input) => { 252 | let items = []; 253 | if (Array.isArray(input.items)) 254 | items = input.items.map(exports.toInvoice); 255 | return items; 256 | }; 257 | exports.toInvoices = toInvoices; 258 | /** 259 | * Convert backend API result to library result object to return in 260 | * {@link Client.getChecks} and {@link Client.getChecksPaginate} 261 | * methods 262 | * 263 | * @param input - Backend API result 264 | * 265 | * @returns Converted result 266 | */ 267 | const toChecks = (input) => { 268 | let items = []; 269 | if (Array.isArray(input.items)) 270 | items = input.items.map(exports.toCheck); 271 | return items; 272 | }; 273 | exports.toChecks = toChecks; 274 | /** 275 | * Convert backend API result to library result object to return in 276 | * {@link Client.getTransfers} and {@link Client.getTransfersPaginate} 277 | * methods 278 | * 279 | * @param input - Backend API result 280 | * 281 | * @returns Converted result 282 | */ 283 | const toTransfers = (input) => { 284 | let items = []; 285 | if (Array.isArray(input.items)) 286 | items = input.items.map(exports.toTransfer); 287 | return items; 288 | }; 289 | exports.toTransfers = toTransfers; 290 | /** 291 | * Convert backend API result to library result object to return in 292 | * {@link Client.getStats} method 293 | * 294 | * @param input - Backend API result 295 | * 296 | * @returns Converted result 297 | */ 298 | const toStats = (input) => ({ 299 | volume: input.volume || '0', 300 | conversion: input.conversion || '0', 301 | uniqueUsersCount: input.unique_users_count || 0, 302 | createdInvoiceCount: input.created_invoice_count || 0, 303 | paidInvoiceCount: input.paid_invoice_count || 0, 304 | startAt: new Date(input.start_at ? input.start_at : 0), 305 | endAt: new Date(input.end_at ? input.end_at : 0), 306 | }); 307 | exports.toStats = toStats; 308 | /** 309 | * Convert backend API result to library result object to return in 310 | * {@link Store.getMe} method 311 | * 312 | * @param input - Backend API result 313 | * 314 | * @returns Converted result 315 | */ 316 | const toMe = (input) => ({ 317 | id: input.app_id || 0, 318 | name: input.name || '', 319 | bot: input.payment_processing_bot_username || '', 320 | }); 321 | exports.toMe = toMe; 322 | -------------------------------------------------------------------------------- /lib/helpers/utils.d.ts: -------------------------------------------------------------------------------- 1 | import { CurrencyType, CryptoCurrencyCode, FiatCurrencyCode, InvoiceStatus, CheckStatus, ExchangeRates } from './casts'; 2 | /** Possible backend API methods names */ 3 | export type ApiMethod = 'getMe' | 'getStats' | 'createInvoice' | 'createCheck' | 'deleteInvoice' | 'deleteCheck' | 'getInvoices' | 'getChecks' | 'getBalance' | 'getExchangeRates' | 'getCurrencies' | 'transfer' | 'getTransfers'; 4 | /** Options object type for {@link Client.getStats} method */ 5 | export type GetStatsOptions = { 6 | /** Date from which start calculating statistics */ 7 | startAt?: Date; 8 | /** The date on which to finish calculating statistics */ 9 | endAt?: Date; 10 | }; 11 | /** Backend options object type for {@link Client.getStats} method */ 12 | export type GetStatsBackendOptions = { 13 | /** Date from which start calculating statistics */ 14 | start_at?: string; 15 | /** The date on which to finish calculating statistics */ 16 | end_at?: string; 17 | }; 18 | /** Options object type for {@link Client.transfer} method */ 19 | export type TransferOptions = { 20 | /** User ID in Telegram */ 21 | userId: number; 22 | /** Transfer asset */ 23 | asset: CryptoCurrencyCode; 24 | /** Transfer amount */ 25 | amount: number | string; 26 | /** 27 | * Random UTF-8 string unique per transfer for idempotent requests. 28 | * The same spend_id can be accepted only once from your app. 29 | * Up to 64 symbols. 30 | */ 31 | spendId: string; 32 | /** 33 | * Comment for the transfer. 34 | * Users will see this comment in the notification about the transfer 35 | */ 36 | comment?: string; 37 | /** Pass true to not send to the user the notification about the transfer */ 38 | disableSendNotification?: boolean; 39 | }; 40 | /** Backend options object type for {@link Client.transfer} method */ 41 | export type TransferBackendOptions = { 42 | /** User ID in Telegram */ 43 | user_id: number; 44 | /** Transfer asset */ 45 | asset: CryptoCurrencyCode; 46 | /** Transfer amount */ 47 | amount: number | string; 48 | /** 49 | * Random UTF-8 string unique per transfer for idempotent requests. 50 | * The same spend_id can be accepted only once from your app. 51 | * Up to 64 symbols. 52 | */ 53 | spend_id: string; 54 | /** 55 | * Comment for the transfer. 56 | * Users will see this comment in the notification about the transfer 57 | */ 58 | comment?: string; 59 | /** Pass true to not send to the user the notification about the transfer */ 60 | disable_send_notification?: boolean; 61 | }; 62 | /** Options object type for {@link Client.createCheck} method */ 63 | export type CreateCheckOptions = { 64 | /** Check asset */ 65 | asset: CryptoCurrencyCode; 66 | /** Check amount */ 67 | amount: number | string; 68 | /** ID of the user who will be able to activate the check */ 69 | pinToUserId?: number; 70 | /** A user with the specified username will be able to activate the check */ 71 | pinToUsername?: string; 72 | }; 73 | /** Backend options object type for {@link Client.createCheck} method */ 74 | export type CreateCheckBackendOptions = { 75 | /** Invoice asset */ 76 | asset: CryptoCurrencyCode; 77 | /** Invoice amount */ 78 | amount: string; 79 | /** ID of the user who will be able to activate the check */ 80 | pin_to_user_id?: number; 81 | /** A user with the specified username will be able to activate the check */ 82 | pin_to_username?: string; 83 | }; 84 | /** Options object type for {@link Client.createInvoice} method */ 85 | export type CreateInvoiceOptions = { 86 | /** Invoice amount */ 87 | amount: number | string; 88 | /** Currency type */ 89 | currencyType?: CurrencyType.Crypto | CurrencyType.Fiat; 90 | /** Invoice asset */ 91 | asset?: CryptoCurrencyCode; 92 | /** Invoice fiat */ 93 | fiat?: FiatCurrencyCode; 94 | /** List of cryptocurrency alphabetic codes */ 95 | acceptedAssets?: CryptoCurrencyCode[]; 96 | /** Invoice description, displayed to user, up to 1024 symbols */ 97 | description?: string; 98 | /** 99 | * Invoice payload, visible only for app, if it not string, JSON.stringify using 100 | * for preparing to backend API parameters, may be up to 4096 symbols after preparing 101 | */ 102 | payload?: any; 103 | /** Url for button which will be shown when invoice was paid */ 104 | paidBtnUrl?: string; 105 | /** Text for button which will be shown when invoice was paid */ 106 | paidBtnName?: PaidBtnName; 107 | /** Is can user leave a comment for invoice */ 108 | isAllowComments?: boolean; 109 | /** Is can user pay invoice anonymously */ 110 | isAllowAnonymous?: boolean; 111 | /** Text of the message which will be presented to a user after the invoice is paid */ 112 | hiddenMessage?: string; 113 | /** You can set a payment time limit for the invoice in seconds */ 114 | expiresIn?: number; 115 | }; 116 | /** Backend options object type for {@link Client.createInvoice} method */ 117 | export type CreateInvoiceBackendOptions = { 118 | /** Invoice amount */ 119 | amount: string; 120 | /** Currency type */ 121 | currency_type?: CurrencyType.Crypto | CurrencyType.Fiat; 122 | /** Invoice asset */ 123 | asset?: CryptoCurrencyCode; 124 | /** Invoice fiat */ 125 | fiat?: FiatCurrencyCode; 126 | /** List of cryptocurrency alphabetic codes separated comma */ 127 | accepted_assets?: string; 128 | /** Invoice description, displayed to user */ 129 | description?: string; 130 | /** Invoice payload, visible only for app */ 131 | payload?: string; 132 | /** Url for button which will be shown when invoice was paid */ 133 | paid_btn_url?: string; 134 | /** Text for button which will be shown when invoice was paid */ 135 | paid_btn_name?: PaidBtnName; 136 | /** Is can user leave a comment for invoice */ 137 | allow_comments?: boolean; 138 | /** Is can user pay invoice anonymously */ 139 | allow_anonymous?: boolean; 140 | /** Text of the message which will be presented to a user after the invoice is paid */ 141 | hidden_message?: string; 142 | /** You can set a payment time limit for the invoice in seconds */ 143 | expires_in?: number; 144 | }; 145 | /** Options object type for {@link Client.getInvoices} method */ 146 | export type GetInvoicesOptions = { 147 | /** Invoices crypto currency filter */ 148 | asset?: CryptoCurrencyCode; 149 | /** Invoices fiat currency filter */ 150 | fiat?: FiatCurrencyCode; 151 | /** Invoices identifiers filter */ 152 | ids?: number[]; 153 | /** Invoices status filter */ 154 | status?: GetInvoicesStatus; 155 | /** Number of invoices to skip */ 156 | offset?: number; 157 | /** Number of invoices returned */ 158 | count?: number; 159 | }; 160 | /** Options object type for {@link Client.getInvoicesPaginate} method */ 161 | export type GetInvoicesPaginateOptions = { 162 | /** Invoices crypto currency filter */ 163 | asset?: CryptoCurrencyCode; 164 | /** Invoices fiat currency filter */ 165 | fiat?: FiatCurrencyCode; 166 | /** Invoices identifiers filter */ 167 | ids?: number[]; 168 | /** Invoices status filter */ 169 | status?: GetInvoicesStatus; 170 | /** Pagination page number */ 171 | page?: number; 172 | }; 173 | /** 174 | * Backend options object type for {@link Client.getInvoices} 175 | * and {@link Client.getInvoicesPaginate} methods 176 | */ 177 | export type GetInvoicesBackendOptions = { 178 | /** Invoices crypto currency filter */ 179 | asset?: CryptoCurrencyCode; 180 | /** Invoices fiat currency filter */ 181 | fiat?: FiatCurrencyCode; 182 | /** Invoices identifiers filter */ 183 | invoice_ids?: string; 184 | /** Invoices status filter */ 185 | status?: GetInvoicesStatus; 186 | /** Number of invoices to skip */ 187 | offset?: number; 188 | /** Number of invoices returned */ 189 | count?: number; 190 | }; 191 | /** Options object type for {@link Client.getChecks} method */ 192 | export type GetChecksOptions = { 193 | /** Checks asset filter */ 194 | asset?: CryptoCurrencyCode; 195 | /** Checks identifiers filter */ 196 | ids?: number[]; 197 | /** Checks status filter */ 198 | status?: GetChecksStatus; 199 | /** Number of checks to skip */ 200 | offset?: number; 201 | /** Number of checks returned */ 202 | count?: number; 203 | }; 204 | /** Options object type for {@link Client.getChecksPaginate} method */ 205 | export type GetChecksPaginateOptions = { 206 | /** Checks asset filter */ 207 | asset?: CryptoCurrencyCode; 208 | /** Checks identifiers filter */ 209 | ids?: number[]; 210 | /** Checks status filter */ 211 | status?: GetChecksStatus; 212 | /** Pagination page number */ 213 | page?: number; 214 | }; 215 | /** 216 | * Backend options object type for {@link Client.getChecks} 217 | * and {@link Client.getChecksPaginate} methods 218 | */ 219 | export type GetChecksBackendOptions = { 220 | /** Checks asset filter */ 221 | asset?: CryptoCurrencyCode; 222 | /** Checks identifiers filter */ 223 | check_ids?: string; 224 | /** Checks status filter */ 225 | status?: GetChecksStatus; 226 | /** Number of checks to skip */ 227 | offset?: number; 228 | /** Number of checks returned */ 229 | count?: number; 230 | }; 231 | /** Options object type for {@link Client.getTransfers} method */ 232 | export type GetTransfersOptions = { 233 | /** Transfer asset filter */ 234 | asset?: CryptoCurrencyCode; 235 | /** Transfers identifiers filter */ 236 | ids?: number[]; 237 | /** Transfer spend identifier */ 238 | spendId?: string; 239 | /** Number of transfers to skip */ 240 | offset?: number; 241 | /** Number of transfers returned */ 242 | count?: number; 243 | }; 244 | /** Options object type for {@link Client.getTransfersPaginate} method */ 245 | export type GetTransfersPaginateOptions = { 246 | /** Transfer asset filter */ 247 | asset?: CryptoCurrencyCode; 248 | /** Transfers identifiers filter */ 249 | ids?: number[]; 250 | /** Transfer spend identifier */ 251 | spendId?: string; 252 | /** Pagination page number */ 253 | page?: number; 254 | }; 255 | /** 256 | * Backend options object type for {@link Client.getTransfers} 257 | * and {@link Client.getTransfersPaginate} methods 258 | */ 259 | export type GetTransfersBackendOptions = { 260 | /** Transfer asset filter */ 261 | asset?: CryptoCurrencyCode; 262 | /** Transfers identifiers filter */ 263 | transfer_ids?: string; 264 | /** Transfer spend identifier */ 265 | spend_id?: string; 266 | /** Number of transfers to skip */ 267 | offset?: number; 268 | /** Number of transfers returned */ 269 | count?: number; 270 | }; 271 | /** 272 | * Possible invoices statuses 273 | * - {@link InvoiceStatus.Active} - Unpaid invoice 274 | * - {@link InvoiceStatus.Paid} - Paid invoice 275 | */ 276 | export type GetInvoicesStatus = InvoiceStatus.Active | InvoiceStatus.Paid; 277 | /** 278 | * Possible checks statuses 279 | * - {@link CheckStatus.Active} - Active check 280 | * - {@link CheckStatus.Activated} - Activated check 281 | */ 282 | export type GetChecksStatus = CheckStatus.Active | CheckStatus.Activated; 283 | /** 284 | * Express.js-like API middleware handler 285 | */ 286 | export type Middleware = (req: any, res: any) => void; 287 | /** 288 | * Paid button types, button text depends on the type 289 | * - viewItem - View Item 290 | * - openChannel - Open Channel 291 | * - openBot - Open Bot 292 | * - callback - Return 293 | */ 294 | export type PaidBtnName = 'viewItem' | 'openChannel' | 'openBot' | 'callback'; 295 | /** 296 | * Return exchange rate to passed currencies pair 297 | * 298 | * @param source - Source currency code 299 | * @param target - Target currency code 300 | * @param exchangeRates - Exchange rates information from {@link Store.getExchangeRates} method 301 | * 302 | * @returns Exchange rate or zero, if currencies pair not exists 303 | */ 304 | export declare const getExchageRate: (source: string, target: string, exchangeRates: ExchangeRates) => string; 305 | /** 306 | * Check is string is valid url 307 | * 308 | * @param input - String 309 | * 310 | * @returns Check result 311 | */ 312 | export declare const isValidUrl: (input: string) => boolean; 313 | /** 314 | * Convert {@link GetStatsOptions} object to using backend API method 315 | * parameters {@link GetStatsBackendOptions} object 316 | * 317 | * @param options - Library {@link Client.getStats} method options object 318 | * 319 | * @throws Error - If options object invalid 320 | * 321 | * @returns Object with corresponding backend API method parameters 322 | */ 323 | export declare const prepareGetStatsOptions: (options: GetStatsOptions) => GetStatsBackendOptions; 324 | /** 325 | * Convert {@link CreateCheckOptions} object to using backend API method 326 | * parameters {@link CreateCheckBackendOptions} object 327 | * 328 | * @param options - Library {@link Client.createCheck} method options object 329 | * 330 | * @throws Error - If options object invalid 331 | * 332 | * @returns Object with corresponding backend API method parameters 333 | */ 334 | export declare const prepareTransferOptions: (options: TransferOptions) => TransferBackendOptions; 335 | /** 336 | * Convert {@link CreateCheckOptions} object to using backend API method 337 | * parameters {@link CreateCheckBackendOptions} object 338 | * 339 | * @param options - Library {@link Client.createCheck} method options object 340 | * 341 | * @throws Error - If options object invalid 342 | * 343 | * @returns Object with corresponding backend API method parameters 344 | */ 345 | export declare const prepareCreateCheckOptions: (options: CreateCheckOptions) => CreateCheckBackendOptions; 346 | /** 347 | * Convert {@link CreateInvoiceOptions} object to using backend API method 348 | * parameters {@link CreateInvoiceBackendOptions} object 349 | * 350 | * @param options - Library {@link Client.createInvoice} method options object 351 | * 352 | * @throws Error - If options object invalid 353 | * 354 | * @returns Object with corresponding backend API method parameters 355 | */ 356 | export declare const prepareCreateInvoiceOptions: (options: CreateInvoiceOptions) => CreateInvoiceBackendOptions; 357 | /** 358 | * Convert identifier to using backend API delete methods 359 | * 360 | * @param id - Passed identifier 361 | * 362 | * @throws Error - If options identifier invalid 363 | * 364 | * @returns Identifier number 365 | */ 366 | export declare const prepareDeleteOptions: (id: any) => number; 367 | /** 368 | * Convert {@link GetInvoicesOptions} object to using backend API method 369 | * parameters {@link GetInvoicesBackendOptions} object 370 | * 371 | * @param options - Library {@link Client.getInvoices} method options object 372 | * 373 | * @returns Object with corresponding backend API method parameters 374 | */ 375 | export declare const prepareGetInvoicesOptions: (options: GetInvoicesOptions) => GetInvoicesBackendOptions; 376 | /** 377 | * Convert {@link GetInvoicesPaginateOptions} object to using backend API method 378 | * parameters {@link GetInvoicesBackendOptions} object 379 | * 380 | * @param options - Library {@link Client.getInvoicesPaginate} method options object 381 | * 382 | * @returns Object with corresponding backend API method parameters 383 | */ 384 | export declare const prepareGetInvoicesPaginateOptions: (pageSize: number, options: GetInvoicesPaginateOptions) => GetInvoicesBackendOptions; 385 | /** 386 | * Convert {@link GetChecksOptions} object to using backend API method 387 | * parameters {@link GetChecksBackendOptions} object 388 | * 389 | * @param options - Library {@link Client.getChecks} method options object 390 | * 391 | * @returns Object with corresponding backend API method parameters 392 | */ 393 | export declare const prepareGetChecksOptions: (options: GetChecksOptions) => GetChecksBackendOptions; 394 | /** 395 | * Convert {@link GetChecksPaginateOptions} object to using backend API method 396 | * parameters {@link GetChecksBackendOptions} object 397 | * 398 | * @param options - Library {@link Client.getChecksPaginate} method options object 399 | * 400 | * @returns Object with corresponding backend API method parameters 401 | */ 402 | export declare const prepareGetChecksPaginateOptions: (pageSize: number, options: GetChecksPaginateOptions) => GetChecksBackendOptions; 403 | /** 404 | * Convert {@link GetTransfersOptions} object to using backend API method 405 | * parameters {@link GetTransfersBackendOptions} object 406 | * 407 | * @param options - Library {@link Client.getTransfers} method options object 408 | * 409 | * @returns Object with corresponding backend API method parameters 410 | */ 411 | export declare const prepareGetTransfersOptions: (options: GetTransfersOptions) => GetTransfersBackendOptions; 412 | /** 413 | * Convert {@link GetTransfersPaginateOptions} object to using backend API method 414 | * parameters {@link GetTransfersBackendOptions} object 415 | * 416 | * @param options - Library {@link Client.getTransfersPaginate} method options object 417 | * 418 | * @returns Object with corresponding backend API method parameters 419 | */ 420 | export declare const prepareGetTransfersPaginateOptions: (pageSize: number, options: GetTransfersPaginateOptions) => GetTransfersBackendOptions; 421 | -------------------------------------------------------------------------------- /lib/helpers/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.prepareGetTransfersPaginateOptions = exports.prepareGetTransfersOptions = exports.prepareGetChecksPaginateOptions = exports.prepareGetChecksOptions = exports.prepareGetInvoicesPaginateOptions = exports.prepareGetInvoicesOptions = exports.prepareDeleteOptions = exports.prepareCreateInvoiceOptions = exports.prepareCreateCheckOptions = exports.prepareTransferOptions = exports.prepareGetStatsOptions = exports.isValidUrl = exports.getExchageRate = void 0; 4 | const casts_1 = require("./casts"); 5 | /** 6 | * Return exchange rate to passed currencies pair 7 | * 8 | * @param source - Source currency code 9 | * @param target - Target currency code 10 | * @param exchangeRates - Exchange rates information from {@link Store.getExchangeRates} method 11 | * 12 | * @returns Exchange rate or zero, if currencies pair not exists 13 | */ 14 | const getExchageRate = (source, target, exchangeRates) => { 15 | let rate = ''; 16 | for (let i = 0, l = exchangeRates.length; i < l; i += 1) { 17 | const exchangeRate = exchangeRates[i]; 18 | // If source and target correspond to direction in Store.getExchangeRates method result 19 | if (exchangeRate.source === source && exchangeRate.target === target) { 20 | if (exchangeRate.isValid) 21 | rate = exchangeRate.rate; 22 | break; 23 | } 24 | } 25 | if (rate === '') 26 | return '0'; 27 | return rate; 28 | }; 29 | exports.getExchageRate = getExchageRate; 30 | /** 31 | * Url check reguar expression 32 | */ 33 | const URL_CHECK_REGEXP = /^https?:\/\/((([a-z\d][a-z\d-]*[a-z\d])\.)+[a-z]{2,}|((\d{1,3}\.){3}\d{1,3}))(:\d+)?(\/[-a-z\d%_.~+]*)*$/i; 34 | /** 35 | * Check is string is valid url 36 | * 37 | * @param input - String 38 | * 39 | * @returns Check result 40 | */ 41 | const isValidUrl = (input) => URL_CHECK_REGEXP.test(input); 42 | exports.isValidUrl = isValidUrl; 43 | /** 44 | * Convert {@link GetStatsOptions} object to using backend API method 45 | * parameters {@link GetStatsBackendOptions} object 46 | * 47 | * @param options - Library {@link Client.getStats} method options object 48 | * 49 | * @throws Error - If options object invalid 50 | * 51 | * @returns Object with corresponding backend API method parameters 52 | */ 53 | const prepareGetStatsOptions = (options) => { 54 | const prepared = {}; 55 | if (options.startAt === undefined && options.endAt === undefined) 56 | return prepared; 57 | if (options.startAt === undefined || !(options.startAt instanceof Date)) { 58 | throw new Error('Field `startAt` must be a Date'); 59 | } 60 | if (options.endAt === undefined || !(options.endAt instanceof Date)) { 61 | throw new Error('Field `endAt` must be a Date'); 62 | } 63 | prepared.start_at = options.startAt.toISOString(); 64 | prepared.end_at = options.endAt.toISOString(); 65 | return prepared; 66 | }; 67 | exports.prepareGetStatsOptions = prepareGetStatsOptions; 68 | /** 69 | * Convert {@link CreateCheckOptions} object to using backend API method 70 | * parameters {@link CreateCheckBackendOptions} object 71 | * 72 | * @param options - Library {@link Client.createCheck} method options object 73 | * 74 | * @throws Error - If options object invalid 75 | * 76 | * @returns Object with corresponding backend API method parameters 77 | */ 78 | const prepareTransferOptions = (options) => { 79 | if (options.comment !== undefined && options.comment.length > 1024) { 80 | throw new Error('Comment can\'t be longer than 1024 characters'); 81 | } 82 | // Create object with required parameters 83 | const prepared = { 84 | user_id: options.userId, 85 | spend_id: options.spendId, 86 | asset: options.asset, 87 | amount: typeof options.amount === 'number' ? '' + options.amount : options.amount, 88 | }; 89 | if (options.disableSendNotification !== undefined) { 90 | prepared.disable_send_notification = options.disableSendNotification; 91 | } 92 | if (options.comment !== undefined) 93 | prepared.comment = options.comment; 94 | return prepared; 95 | }; 96 | exports.prepareTransferOptions = prepareTransferOptions; 97 | /** 98 | * Convert {@link CreateCheckOptions} object to using backend API method 99 | * parameters {@link CreateCheckBackendOptions} object 100 | * 101 | * @param options - Library {@link Client.createCheck} method options object 102 | * 103 | * @throws Error - If options object invalid 104 | * 105 | * @returns Object with corresponding backend API method parameters 106 | */ 107 | const prepareCreateCheckOptions = (options) => { 108 | // Create object with required parameters 109 | const prepared = { 110 | asset: options.asset, 111 | amount: typeof options.amount === 'number' ? '' + options.amount : options.amount, 112 | }; 113 | if (options.pinToUserId !== undefined) 114 | prepared.pin_to_user_id = options.pinToUserId; 115 | if (options.pinToUsername !== undefined) 116 | prepared.pin_to_username = options.pinToUsername; 117 | if (options.pinToUserId !== undefined && options.pinToUsername !== undefined) { 118 | throw new Error('Pass only one of `pinToUserId` and `pinToUsername`'); 119 | } 120 | return prepared; 121 | }; 122 | exports.prepareCreateCheckOptions = prepareCreateCheckOptions; 123 | /** 124 | * Convert {@link CreateInvoiceOptions} object to using backend API method 125 | * parameters {@link CreateInvoiceBackendOptions} object 126 | * 127 | * @param options - Library {@link Client.createInvoice} method options object 128 | * 129 | * @throws Error - If options object invalid 130 | * 131 | * @returns Object with corresponding backend API method parameters 132 | */ 133 | const prepareCreateInvoiceOptions = (options) => { 134 | // Check is options object valid 135 | if (options.description !== undefined && options.description.length > 1024) { 136 | throw new Error('Description can\'t be longer than 1024 characters'); 137 | } 138 | if (options.paidBtnName !== undefined && !options.paidBtnUrl) { 139 | throw new Error('Require paidBtnUrl parameter if paidBtnName parameter pass'); 140 | } 141 | if (options.hiddenMessage !== undefined && options.hiddenMessage.length > 2048) { 142 | throw new Error('Hidden message can\'t be longer than 2048 characters'); 143 | } 144 | if (options.expiresIn !== undefined 145 | && (typeof options.expiresIn !== 'number' 146 | || options.expiresIn < 1 147 | || options.expiresIn > 2678400)) { 148 | throw new Error('Expires must be a number between 1-2678400'); 149 | } 150 | let payload; 151 | if (options.payload !== undefined) { 152 | if (typeof options.payload === 'string') 153 | payload = options.payload; 154 | else 155 | payload = JSON.stringify(options.payload); 156 | if (payload.length > 4096) { 157 | throw new Error('Payload can\'t be longer than 4096 characters'); 158 | } 159 | } 160 | // Create object with required parameters 161 | const prepared = { 162 | amount: typeof options.amount === 'number' ? '' + options.amount : options.amount, 163 | }; 164 | const currencyType = options.currencyType || casts_1.CurrencyType.Crypto; 165 | prepared.currency_type = currencyType; 166 | if (currencyType === casts_1.CurrencyType.Crypto) { 167 | const asset = options.asset; 168 | if (!asset) 169 | throw new Error('Field `asset` required for crypto currency type'); 170 | prepared.asset = asset; 171 | } 172 | if (currencyType === casts_1.CurrencyType.Fiat) { 173 | const fiat = options.fiat; 174 | if (!fiat) 175 | throw new Error('Field `fiat` required for fiat currency type'); 176 | prepared.fiat = fiat; 177 | if (options.acceptedAssets !== undefined) { 178 | if (!Array.isArray(options.acceptedAssets)) { 179 | throw new Error('Field `acceptedAssets` must be array'); 180 | } 181 | prepared.accepted_assets = options.acceptedAssets.join(','); 182 | } 183 | } 184 | // Same names 185 | if (options.expiresIn !== undefined) 186 | prepared.expires_in = options.expiresIn; 187 | if (options.description !== undefined) 188 | prepared.description = options.description; 189 | if (options.hiddenMessage !== undefined) 190 | prepared.hidden_message = options.hiddenMessage; 191 | if (payload !== undefined) 192 | prepared.payload = payload; 193 | // Different names 194 | if (options.paidBtnUrl !== undefined) 195 | prepared.paid_btn_url = options.paidBtnUrl; 196 | if (options.paidBtnName !== undefined) 197 | prepared.paid_btn_name = options.paidBtnName; 198 | if (options.isAllowComments !== undefined) 199 | prepared.allow_comments = options.isAllowComments; 200 | if (options.isAllowAnonymous !== undefined) 201 | prepared.allow_anonymous = options.isAllowAnonymous; 202 | return prepared; 203 | }; 204 | exports.prepareCreateInvoiceOptions = prepareCreateInvoiceOptions; 205 | /** 206 | * Convert identifier to using backend API delete methods 207 | * 208 | * @param id - Passed identifier 209 | * 210 | * @throws Error - If options identifier invalid 211 | * 212 | * @returns Identifier number 213 | */ 214 | const prepareDeleteOptions = (id) => { 215 | if (typeof id !== 'number' || isNaN(id) || id < 1) { 216 | throw new Error('Identifier must be a valid positive number'); 217 | } 218 | return id; 219 | }; 220 | exports.prepareDeleteOptions = prepareDeleteOptions; 221 | /** 222 | * Convert {@link GetInvoicesOptions} object to using backend API method 223 | * parameters {@link GetInvoicesBackendOptions} object 224 | * 225 | * @param options - Library {@link Client.getInvoices} method options object 226 | * 227 | * @returns Object with corresponding backend API method parameters 228 | */ 229 | const prepareGetInvoicesOptions = (options) => { 230 | // Create empty object, method doesn't have required parameters 231 | const prepared = {}; 232 | // Same names 233 | if (options.status !== undefined) 234 | prepared.status = options.status; 235 | if (options.offset !== undefined) 236 | prepared.offset = options.offset; 237 | if (options.count !== undefined) 238 | prepared.count = options.count; 239 | // Different names 240 | if (options.asset !== undefined) 241 | prepared.asset = options.asset; 242 | if (options.fiat !== undefined) 243 | prepared.fiat = options.fiat; 244 | if (options.ids !== undefined) 245 | prepared.invoice_ids = options.ids.join(','); 246 | return prepared; 247 | }; 248 | exports.prepareGetInvoicesOptions = prepareGetInvoicesOptions; 249 | /** 250 | * Convert {@link GetInvoicesPaginateOptions} object to using backend API method 251 | * parameters {@link GetInvoicesBackendOptions} object 252 | * 253 | * @param options - Library {@link Client.getInvoicesPaginate} method options object 254 | * 255 | * @returns Object with corresponding backend API method parameters 256 | */ 257 | const prepareGetInvoicesPaginateOptions = (pageSize, options) => { 258 | // Create empty object, method doesn't have required parameters 259 | const prepared = {}; 260 | // Same names 261 | if (options.status !== undefined) 262 | prepared.status = options.status; 263 | // Different names 264 | if (options.asset !== undefined) 265 | prepared.asset = options.asset; 266 | if (options.fiat !== undefined) 267 | prepared.fiat = options.fiat; 268 | if (options.ids !== undefined) 269 | prepared.invoice_ids = options.ids.join(','); 270 | // Paginate options 271 | let page = options.page ? +options.page : 1; 272 | if (page < 1) 273 | page = 1; 274 | prepared.count = pageSize; 275 | prepared.offset = pageSize * (page - 1); 276 | return prepared; 277 | }; 278 | exports.prepareGetInvoicesPaginateOptions = prepareGetInvoicesPaginateOptions; 279 | /** 280 | * Convert {@link GetChecksOptions} object to using backend API method 281 | * parameters {@link GetChecksBackendOptions} object 282 | * 283 | * @param options - Library {@link Client.getChecks} method options object 284 | * 285 | * @returns Object with corresponding backend API method parameters 286 | */ 287 | const prepareGetChecksOptions = (options) => { 288 | // Create empty object, method doesn't have required parameters 289 | const prepared = {}; 290 | // Same names 291 | if (options.status !== undefined) 292 | prepared.status = options.status; 293 | if (options.offset !== undefined) 294 | prepared.offset = options.offset; 295 | if (options.count !== undefined) 296 | prepared.count = options.count; 297 | // Different names 298 | if (options.asset !== undefined) 299 | prepared.asset = options.asset; 300 | if (options.ids !== undefined) 301 | prepared.check_ids = options.ids.join(','); 302 | return prepared; 303 | }; 304 | exports.prepareGetChecksOptions = prepareGetChecksOptions; 305 | /** 306 | * Convert {@link GetChecksPaginateOptions} object to using backend API method 307 | * parameters {@link GetChecksBackendOptions} object 308 | * 309 | * @param options - Library {@link Client.getChecksPaginate} method options object 310 | * 311 | * @returns Object with corresponding backend API method parameters 312 | */ 313 | const prepareGetChecksPaginateOptions = (pageSize, options) => { 314 | // Create empty object, method doesn't have required parameters 315 | const prepared = {}; 316 | // Same names 317 | if (options.status !== undefined) 318 | prepared.status = options.status; 319 | // Different names 320 | if (options.asset !== undefined) 321 | prepared.asset = options.asset; 322 | if (options.ids !== undefined) 323 | prepared.check_ids = options.ids.join(','); 324 | // Paginate options 325 | let page = options.page ? +options.page : 1; 326 | if (page < 1) 327 | page = 1; 328 | prepared.count = pageSize; 329 | prepared.offset = pageSize * (page - 1); 330 | return prepared; 331 | }; 332 | exports.prepareGetChecksPaginateOptions = prepareGetChecksPaginateOptions; 333 | /** 334 | * Convert {@link GetTransfersOptions} object to using backend API method 335 | * parameters {@link GetTransfersBackendOptions} object 336 | * 337 | * @param options - Library {@link Client.getTransfers} method options object 338 | * 339 | * @returns Object with corresponding backend API method parameters 340 | */ 341 | const prepareGetTransfersOptions = (options) => { 342 | // Create empty object, method doesn't have required parameters 343 | const prepared = {}; 344 | // Same names 345 | if (options.offset !== undefined) 346 | prepared.offset = options.offset; 347 | if (options.count !== undefined) 348 | prepared.count = options.count; 349 | // Different names 350 | if (options.asset !== undefined) 351 | prepared.asset = options.asset; 352 | if (options.spendId !== undefined) 353 | prepared.spend_id = options.spendId; 354 | if (options.ids !== undefined) 355 | prepared.transfer_ids = options.ids.join(','); 356 | return prepared; 357 | }; 358 | exports.prepareGetTransfersOptions = prepareGetTransfersOptions; 359 | /** 360 | * Convert {@link GetTransfersPaginateOptions} object to using backend API method 361 | * parameters {@link GetTransfersBackendOptions} object 362 | * 363 | * @param options - Library {@link Client.getTransfersPaginate} method options object 364 | * 365 | * @returns Object with corresponding backend API method parameters 366 | */ 367 | const prepareGetTransfersPaginateOptions = (pageSize, options) => { 368 | // Create empty object, method doesn't have required parameters 369 | const prepared = {}; 370 | // Different names 371 | if (options.asset !== undefined) 372 | prepared.asset = options.asset; 373 | if (options.spendId !== undefined) 374 | prepared.spend_id = options.spendId; 375 | if (options.ids !== undefined) 376 | prepared.transfer_ids = options.ids.join(','); 377 | // Paginate options 378 | let page = options.page ? +options.page : 1; 379 | if (page < 1) 380 | page = 1; 381 | prepared.count = pageSize; 382 | prepared.offset = pageSize * (page - 1); 383 | return prepared; 384 | }; 385 | exports.prepareGetTransfersPaginateOptions = prepareGetTransfersPaginateOptions; 386 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import Client from './classes/ClientEmitter'; 2 | export = Client; 3 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const ClientEmitter_1 = require("./classes/ClientEmitter"); 3 | module.exports = ClientEmitter_1.default; 4 | -------------------------------------------------------------------------------- /lib/request/http.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Make HTTP GET request 3 | * 4 | * Module imported for Node.js library building 5 | * 6 | * @param url - Url 7 | * @param apiKey - Crypto Bot API key 8 | * 9 | * @throws Error - If request fail 10 | * 11 | * @returns Raw response text 12 | */ 13 | declare const request: (url: string, apiKey: string) => Promise; 14 | export default request; 15 | -------------------------------------------------------------------------------- /lib/request/http.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const https_1 = require("https"); 4 | /** 5 | * Make HTTP GET request 6 | * 7 | * Module imported for Node.js library building 8 | * 9 | * @param url - Url 10 | * @param apiKey - Crypto Bot API key 11 | * 12 | * @throws Error - If request fail 13 | * 14 | * @returns Raw response text 15 | */ 16 | const request = (url, apiKey) => new Promise((resolve, reject) => { 17 | const options = { 18 | headers: { 'Crypto-Pay-API-Token': apiKey }, 19 | }; 20 | const req = (0, https_1.request)(url, options, (res) => { 21 | let data = ''; 22 | res.on('error', reject); 23 | res.on('data', (chunk) => { 24 | data += chunk; 25 | }); 26 | res.on('end', () => { 27 | resolve(data); 28 | }); 29 | }); 30 | req.on('error', reject); 31 | req.end(); 32 | }); 33 | exports.default = request; 34 | -------------------------------------------------------------------------------- /lib/request/xhr.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Make HTTP GET request 3 | * 4 | * Module imported only for browsers bundle 5 | * 6 | * @param url - Url 7 | * @param apiKey - Crypto Bot API key 8 | * 9 | * @throws Error - If request fail 10 | * 11 | * @returns Raw response text 12 | */ 13 | declare const request: (url: string, apiKey: string) => Promise; 14 | export default request; 15 | -------------------------------------------------------------------------------- /lib/request/xhr.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | /** 4 | * Make HTTP GET request 5 | * 6 | * Module imported only for browsers bundle 7 | * 8 | * @param url - Url 9 | * @param apiKey - Crypto Bot API key 10 | * 11 | * @throws Error - If request fail 12 | * 13 | * @returns Raw response text 14 | */ 15 | const request = (url, apiKey) => new Promise((resolve, reject) => { 16 | const xhr = new XMLHttpRequest(); 17 | xhr.open('GET', url, true); 18 | xhr.setRequestHeader('Crypto-Pay-API-Token', apiKey); 19 | xhr.onreadystatechange = () => { 20 | if (xhr.readyState !== 4) 21 | return; 22 | resolve(xhr.responseText); 23 | }; 24 | xhr.onerror = () => { 25 | reject(new Error('Network Error')); 26 | }; 27 | xhr.send(); 28 | }); 29 | exports.default = request; 30 | -------------------------------------------------------------------------------- /lib/types.d.ts: -------------------------------------------------------------------------------- 1 | export { ListenOptions } from 'net'; 2 | export { ServerOptions as httpServerOptions } from 'http'; 3 | export { Server, ServerOptions } from 'https'; 4 | export { SecureContextOptions, TlsOptions } from 'tls'; 5 | export { default as Client } from './classes/Client'; 6 | export { default as ClientEmitter, checkSignature, readRequestBody } from './classes/ClientEmitter'; 7 | export { default as Store, createFetchHandler } from './classes/Store'; 8 | export { default as Transport } from './classes/Transport'; 9 | export * from './helpers/casts'; 10 | export * from './helpers/utils'; 11 | export { default as requestHttp } from './request/http'; 12 | export { default as requestXhr } from './request/xhr'; 13 | import ClientEmitter from './classes/ClientEmitter'; 14 | export default ClientEmitter; 15 | -------------------------------------------------------------------------------- /lib/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __exportStar = (this && this.__exportStar) || function(m, exports) { 14 | for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); 15 | }; 16 | Object.defineProperty(exports, "__esModule", { value: true }); 17 | exports.requestXhr = exports.requestHttp = exports.Transport = exports.createFetchHandler = exports.Store = exports.readRequestBody = exports.checkSignature = exports.ClientEmitter = exports.Client = exports.Server = void 0; 18 | var https_1 = require("https"); 19 | Object.defineProperty(exports, "Server", { enumerable: true, get: function () { return https_1.Server; } }); 20 | var Client_1 = require("./classes/Client"); 21 | Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return Client_1.default; } }); 22 | var ClientEmitter_1 = require("./classes/ClientEmitter"); 23 | Object.defineProperty(exports, "ClientEmitter", { enumerable: true, get: function () { return ClientEmitter_1.default; } }); 24 | Object.defineProperty(exports, "checkSignature", { enumerable: true, get: function () { return ClientEmitter_1.checkSignature; } }); 25 | Object.defineProperty(exports, "readRequestBody", { enumerable: true, get: function () { return ClientEmitter_1.readRequestBody; } }); 26 | var Store_1 = require("./classes/Store"); 27 | Object.defineProperty(exports, "Store", { enumerable: true, get: function () { return Store_1.default; } }); 28 | Object.defineProperty(exports, "createFetchHandler", { enumerable: true, get: function () { return Store_1.createFetchHandler; } }); 29 | var Transport_1 = require("./classes/Transport"); 30 | Object.defineProperty(exports, "Transport", { enumerable: true, get: function () { return Transport_1.default; } }); 31 | __exportStar(require("./helpers/casts"), exports); 32 | __exportStar(require("./helpers/utils"), exports); 33 | var http_1 = require("./request/http"); 34 | Object.defineProperty(exports, "requestHttp", { enumerable: true, get: function () { return http_1.default; } }); 35 | var xhr_1 = require("./request/xhr"); 36 | Object.defineProperty(exports, "requestXhr", { enumerable: true, get: function () { return xhr_1.default; } }); 37 | const ClientEmitter_2 = require("./classes/ClientEmitter"); 38 | exports.default = ClientEmitter_2.default; 39 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021, 2024, 2025 Sergei Ivankov 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. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crypto-bot-api", 3 | "version": "0.3.5", 4 | "description": "Simple and minimalistic client for Telegram CryptoBot Crypto Pay API", 5 | "author": "Sergei Ivankov ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/sergeiivankov/crypto-bot-api" 10 | }, 11 | "exports": { 12 | "types": "./lib/types.d.ts", 13 | "require": "./lib/index.js", 14 | "default": "./lib/index.js" 15 | }, 16 | "files": [ 17 | "lib/**/*" 18 | ], 19 | "scripts": { 20 | "build": "npm run build-docs && npm run build-lib && npm run build-dist", 21 | "build-docs": "typedoc", 22 | "build-lib": "tsc", 23 | "build-dist": "rollup -c", 24 | "watch": "run-p watch-docs watch-lib watch-dist", 25 | "watch-docs": "typedoc --watch --preserveWatchOutput", 26 | "watch-lib": "tsc -w", 27 | "watch-dist": "rollup -c -w", 28 | "lint": "eslint" 29 | }, 30 | "devDependencies": { 31 | "@eslint/js": "^9.23.0", 32 | "@rollup/plugin-replace": "^6.0.2", 33 | "@rollup/plugin-terser": "^0.4.4", 34 | "@rollup/plugin-typescript": "^12.1.2", 35 | "@stylistic/eslint-plugin": "^4.2.0", 36 | "@types/node": "^22.13.12", 37 | "eslint": "^9.23.0", 38 | "eslint-plugin-tsdoc": "^0.4.0", 39 | "npm-run-all": "^4.1.5", 40 | "rollup": "^4.37.0", 41 | "tslib": "^2.8.1", 42 | "typedoc": "^0.28.1", 43 | "typescript": "^5.8.2", 44 | "typescript-eslint": "^8.27.0" 45 | }, 46 | "keywords": [ 47 | "crypto-bot-api", 48 | "crypto-bot", 49 | "crypto-pay-api", 50 | "crypto-pay", 51 | "api-client", 52 | "crypto", 53 | "cryptocurrency", 54 | "exchage", 55 | "payment-processing", 56 | "cryptocurrency-processing", 57 | "ton", 58 | "the-open-network", 59 | "telegram-bot" 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Crypto Bot API - client library for Crypto Pay API 2 | 3 | Written in TypeScript up-to-date client library for Crypto Pay API provided by Telegram [CryptoBot](https://t.me/CryptoBot). 4 | 5 | ## Installing 6 | 7 | Using npm/yarn: 8 | ```bash 9 | $ npm install crypto-bot-api 10 | ``` 11 | 12 | ```bash 13 | $ yarn add crypto-bot-api 14 | ``` 15 | 16 | For browsers use bundles from `dist` directory or add package to your project dependencies and import it. 17 | 18 | ## Usage 19 | 20 | In Node.js: 21 | 22 | ```javascript 23 | const CryptoBotAPI = require('crypto-bot-api'); 24 | 25 | const client = new CryptoBotAPI('1234:AAA...AAA'); 26 | 27 | const me = await client.getMe(); 28 | console.log(me); 29 | ``` 30 | 31 | In browsers: 32 | 33 | ```html 34 | 35 | 42 | ``` 43 | 44 | **Important: at the time of publication of version 0.3.5 (Mar 24, 2025), API servers do not return header Access-Control-Allow-Origin, which allows make requests to API from third-party domains, so client request from website environment won't work (but its work in browser extensions, Electron and similar apps)** 45 | 46 | More usage examples see in [examples](https://github.com/sergeiivankov/crypto-bot-api/tree/main/examples) project directory. 47 | 48 | ## Receiving updates 49 | 50 | Crypto Pay API support events by sending webhooks requests. To handle webhooks request library proposes to use [creating Node.js built-in HTTP or HTTPS server](https://sergeiivankov.github.io/crypto-bot-api/classes/ClientEmitter.html#createServer) or [using Express.js-like middleware API](https://sergeiivankov.github.io/crypto-bot-api/classes/ClientEmitter.html#middleware). See [examples](https://github.com/sergeiivankov/crypto-bot-api/tree/main/examples) with names starts with `webhooks-`. 51 | 52 | *Note: you need enable webhooks in CryptoBot app settings, if you want to use self-signed certificate you must uploat it in CryptoBot API application settings* 53 | 54 | ## Documentation 55 | 56 | Library documentation can be found in [repository GitHub page](https://sergeiivankov.github.io/crypto-bot-api/). 57 | 58 | For Node.js usage, we advise you to start studying documentation with library default exported [ClientEmitter class](https://sergeiivankov.github.io/crypto-bot-api/classes/ClientEmitter.html). 59 | 60 | For browsers usage, we advise you to start studying documentation with library default exported for browsers [Client class](https://sergeiivankov.github.io/crypto-bot-api/classes/Client.html). 61 | 62 | ## Building 63 | 64 | Files for Node.js compiled to `lib` directory. Browsers bundles compiled to `dist` directory. 65 | 66 | ```bash 67 | $ git clone https://github.com/sergeiivankov/crypto-bot-api 68 | $ cd crypto-bot-api 69 | $ npm i 70 | $ npm run build-docs # To build library documentation 71 | $ npm run build-lib # To build for Node.js 72 | $ npm run build-dist # To build for Browsers 73 | $ npm run build # To build all 74 | ``` 75 | 76 | Also, project have `watch` commands to using it in development: 77 | ```bash 78 | $ npm run watch-docs # To watch build library documentation 79 | $ npm run watch-lib # To watch build for Node.js 80 | $ npm run watch-dist # To watch build for Browsers 81 | $ npm run watch # To watch all 82 | ``` 83 | 84 | ## Resources 85 | 86 | * [Documentation](https://sergeiivankov.github.io/crypto-bot-api/) 87 | * [Examples](https://github.com/sergeiivankov/crypto-bot-api/tree/main/examples) 88 | * [Changelog](https://github.com/sergeiivankov/crypto-bot-api/blob/main/changelog.md) 89 | * [Backend CryptoBot API description](https://help.crypt.bot/crypto-pay-api) 90 | 91 | ## Code quality 92 | 93 | To maintain high quality of the code and bring source code to a consistent form, project use `eslint` linter and has high documentation requirements. If you want to make a pull request, check that documentation matches your changes and `eslint` does not signal errors with command: 94 | 95 | ```bash 96 | $ npm run lint 97 | ``` 98 | 99 | ## Supported environments 100 | 101 | - \>= Node.js 12 102 | - \>= Chrome 32 103 | - \>= Firefox 29 104 | - \>= Edge 12 105 | - \>= Safari 8 106 | - \>= Safari on iOS 8 107 | - \> Android Browser 4.4.4 108 | 109 | ## License 110 | 111 | [MIT](https://github.com/sergeiivankov/crypto-bot-api/blob/main/license) -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import replace from '@rollup/plugin-replace'; 2 | import terser from '@rollup/plugin-terser'; 3 | import typescript from '@rollup/plugin-typescript'; 4 | 5 | const plugins = [ 6 | replace({ 7 | delimiters: ['', ''], 8 | preventAssignment: false, 9 | 'request/http': 'request/xhr', 10 | 'import Client from \'./classes/ClientEmitter\'': 'import Client from \'./classes/Client\';export default Client;', 11 | }), 12 | typescript({ 13 | compilerOptions: { 14 | module: 'esnext', 15 | target: 'es5', 16 | sourceMap: true, 17 | declaration: false, 18 | outDir: './dist', 19 | }, 20 | }), 21 | ]; 22 | 23 | const onwarn = (message, handler) => { 24 | if (/TS18028/.test(message)) return; 25 | if (/TS1203/.test(message)) return; 26 | handler(message); 27 | }; 28 | 29 | export default [ 30 | { 31 | input: 'src/index.ts', 32 | output: { 33 | name: 'CryptoBotAPI', 34 | file: 'dist/crypto-bot-api.js', 35 | format: 'iife', 36 | sourcemap: true, 37 | }, 38 | plugins, 39 | onwarn, 40 | watch: { clearScreen: false }, 41 | }, 42 | { 43 | input: 'src/index.ts', 44 | output: { 45 | name: 'CryptoBotAPI', 46 | file: 'dist/crypto-bot-api.min.js', 47 | format: 'iife', 48 | sourcemap: true, 49 | }, 50 | plugins: [ 51 | ...plugins, 52 | terser({ format: { comments: false } }), 53 | ], 54 | onwarn, 55 | watch: false, 56 | }, 57 | ]; 58 | -------------------------------------------------------------------------------- /src/classes/Client.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Balances, Balance, BalancesType, Currency, CurrencyCode, CryptoCurrencyCode, CurrencyType, 3 | Currencies, DetailedCurrencyType, ExchangeRates, Invoice, Check, Stats, Transfer, InvoiceStatus, 4 | CheckStatus, TransferStatus, toBalances, toInvoice, toCheck, toTransfer, toInvoices, toChecks, 5 | toTransfers, toStats, 6 | } from '../helpers/casts'; 7 | import { 8 | ApiMethod, CreateInvoiceOptions, CreateCheckOptions, GetChecksOptions, GetChecksPaginateOptions, 9 | GetInvoicesOptions, GetInvoicesPaginateOptions, GetStatsOptions, TransferOptions, 10 | GetTransfersOptions, GetTransfersPaginateOptions, getExchageRate, 11 | prepareCreateInvoiceOptions, prepareDeleteOptions, prepareGetInvoicesOptions, 12 | prepareGetInvoicesPaginateOptions, prepareGetStatsOptions, prepareCreateCheckOptions, 13 | prepareGetChecksOptions, prepareGetChecksPaginateOptions, prepareTransferOptions, 14 | prepareGetTransfersOptions, prepareGetTransfersPaginateOptions, 15 | } from '../helpers/utils'; 16 | import Store from './Store'; 17 | 18 | // Because `tsdoc` not support `@category` tag, but `typedoc` support 19 | /* eslint-disable tsdoc/syntax */ 20 | /** 21 | * Main class for work with API for browsers 22 | * 23 | * Library for browsers default export this class 24 | * 25 | * @category External 26 | */ 27 | /* eslint-enable tsdoc/syntax */ 28 | export default class Client extends Store { 29 | /** Page size for {@link Client.getInvoicesPaginate} method */ 30 | private _pageSize: number = 100; 31 | 32 | /** 33 | * Access to {@link CurrencyType} enumeration, used in {@link Invoice} type 34 | */ 35 | public static CurrencyType: typeof CurrencyType = CurrencyType; 36 | 37 | /** 38 | * Access to {@link DetailedCurrencyType} enumeration, used in {@link Store.getCurrencies} 39 | * and {@link Client.getCurrency} methods results 40 | */ 41 | public static DetailedCurrencyType: typeof DetailedCurrencyType = DetailedCurrencyType; 42 | 43 | /** 44 | * Access to {@link InvoiceStatus} enumeration, used in {@link Invoice} type, 45 | * {@link Client.getInvoices} and {@link Client.getInvoicesPaginate} methods options 46 | */ 47 | public static InvoiceStatus: typeof InvoiceStatus = InvoiceStatus; 48 | 49 | /** 50 | * Access to {@link CheckStatus} enumeration, used in {@link Check} type, 51 | * {@link Client.getChecks} and {@link Client.getChecksPaginate} methods options 52 | */ 53 | public static CheckStatus: typeof CheckStatus = CheckStatus; 54 | 55 | /** 56 | * Access to {@link TransferStatus} enumeration, used in {@link Transfer} type, 57 | * {@link Client.getTransfers} and {@link Client.getTransfersPaginate} methods options 58 | */ 59 | public static TransferStatus: typeof TransferStatus = TransferStatus; 60 | 61 | /** 62 | * Return count invoices per page for {@link Client.getInvoicesPaginate} method 63 | */ 64 | getPageSize(): number { 65 | return this._pageSize; 66 | } 67 | 68 | /** 69 | * Set count invoices per page for {@link Client.getInvoicesPaginate} method 70 | * 71 | * @param pageSizeParam - Invoices per page 72 | * 73 | * @throws Error - If `pageSize` parameter is invalid 74 | */ 75 | setPageSize(pageSizeParam: number): void { 76 | const pageSize = +pageSizeParam; 77 | if (pageSize > 1000 || pageSize < 1) { 78 | throw Error('Page size may be from 1 to 1000'); 79 | } 80 | this._pageSize = pageSize; 81 | } 82 | 83 | /** 84 | * Get associated with passed API key app statistics 85 | * 86 | * Use {@link toStats} backend API result convert function 87 | * 88 | * @param options - New receive statistics options 89 | * 90 | * @throws Error - If there is an error sending request to backend API or parsing response 91 | * 92 | * @returns Promise, what resolved to associated with passed API key app statistics object 93 | */ 94 | getStats(options: GetStatsOptions = {}): Promise { 95 | return this._transport.call('getStats', prepareGetStatsOptions(options)) 96 | .then((result: any): Stats => toStats(result)); 97 | } 98 | 99 | /** 100 | * Get API app balances infomation 101 | * 102 | * Use {@link toBalances} backend API result convert function 103 | * 104 | * @throws Error - If there is an error sending request to backend API or parsing response 105 | * 106 | * @returns Promise, what resolved to API app balances infomation object 107 | */ 108 | getBalances(): Promise { 109 | return this._transport.call('getBalance').then((result: any): Balances => toBalances(result)); 110 | } 111 | 112 | /** 113 | * Get API app balances infomation 114 | * 115 | * Use {@link toBalances} backend API result convert function 116 | * 117 | * @throws Error - If there is an error sending request to backend API or parsing response 118 | * 119 | * @returns Promise, what resolved to API app available balances infomation object 120 | */ 121 | getBalancesAvailable(): Promise { 122 | return this.getBalances() 123 | .then((balances: Balances): BalancesType => { 124 | return Object.entries(balances).reduce( 125 | (accumulator: BalancesType, entry: [CryptoCurrencyCode, Balance]): BalancesType => { 126 | const [code, balance] = entry; 127 | accumulator[code] = balance.available; 128 | return accumulator; 129 | }, 130 | {} as BalancesType, 131 | ); 132 | }); 133 | } 134 | 135 | /** 136 | * Get API app balances infomation 137 | * 138 | * Use {@link toBalances} backend API result convert function 139 | * 140 | * @throws Error - If there is an error sending request to backend API or parsing response 141 | * 142 | * @returns Promise, what resolved to API app balances on hold infomation object 143 | */ 144 | getBalancesOnhold(): Promise { 145 | return this.getBalances() 146 | .then((balances: Balances): BalancesType => { 147 | return Object.entries(balances).reduce( 148 | (accumulator: BalancesType, entry: [CryptoCurrencyCode, Balance]): BalancesType => { 149 | const [code, balance] = entry; 150 | accumulator[code] = balance.onhold; 151 | return accumulator; 152 | }, 153 | {} as BalancesType, 154 | ); 155 | }); 156 | } 157 | 158 | /** 159 | * Get API app balance value for passed currency 160 | * 161 | * Call {@link Client.getBalances} method to fetch balances information 162 | * 163 | * @param currencyCode - Crypto currency code 164 | * 165 | * @throws Error - If there is an error sending request to backend API or parsing response 166 | * 167 | * @returns Promise, what resolved to API app balance value for passed currency 168 | */ 169 | getBalance(currencyCode: CryptoCurrencyCode): Promise { 170 | return this.getBalances() 171 | .then((balances: Balances): Balance => { 172 | if (balances[currencyCode] === undefined) return { available: '0', onhold: '0' }; 173 | return balances[currencyCode]; 174 | }); 175 | } 176 | 177 | /** 178 | * Get API app balance value for passed currency 179 | * 180 | * Call {@link Client.getBalances} method to fetch balances information 181 | * 182 | * @param currencyCode - Crypto currency code 183 | * 184 | * @throws Error - If there is an error sending request to backend API or parsing response 185 | * 186 | * @returns Promise, what resolved to API app available balance value for passed currency 187 | */ 188 | getBalanceAvailable(currencyCode: CryptoCurrencyCode): Promise { 189 | return this.getBalances() 190 | .then((balances: Balances): string => { 191 | if (balances[currencyCode] === undefined) return '0'; 192 | return balances[currencyCode].available; 193 | }); 194 | } 195 | 196 | /** 197 | * Get API app balance value for passed currency 198 | * 199 | * Call {@link Client.getBalances} method to fetch balances information 200 | * 201 | * @param currencyCode - Crypto currency code 202 | * 203 | * @throws Error - If there is an error sending request to backend API or parsing response 204 | * 205 | * @returns Promise, what resolved to API app balance on hold value for passed currency 206 | */ 207 | getBalanceOnhold(currencyCode: CryptoCurrencyCode): Promise { 208 | return this.getBalances() 209 | .then((balances: Balances): string => { 210 | if (balances[currencyCode] === undefined) return '0'; 211 | return balances[currencyCode].onhold; 212 | }); 213 | } 214 | 215 | /** 216 | * Get currency with passed code infomation 217 | * 218 | * Call {@link Store.getCurrencies} method to fetch currencies information 219 | * 220 | * @param currencyCode - Currency code 221 | * @param isForce - If true, return fresh data from backend API, not from cache 222 | * 223 | * @throws Error - If there is an error sending request to backend API or parsing response 224 | * 225 | * @returns Promise, what resolved to currency with passed code infomation object 226 | * or null, if currency with passed code not exists 227 | */ 228 | getCurrency(currencyCode: CurrencyCode, isForce: boolean = false): Promise { 229 | return this.getCurrencies(isForce) 230 | .then((currencies: Currencies): Currency | null => { 231 | if (currencies[currencyCode] === undefined) return null; 232 | return currencies[currencyCode]; 233 | }); 234 | } 235 | 236 | /** 237 | * Get one exchange rate infomation to passed currencies pair 238 | * 239 | * Call {@link Store.getExchangeRates} method to fetch exchange rates information, 240 | * {@link Store.getCurrencies} method to fetch currencies information 241 | * and use {@link getExchageRate} function to get signle exchange rate 242 | * 243 | * @param source - Source currency code 244 | * @param target - Target currency code 245 | * @param isForce - If true, return fresh data from backend API, not from cache 246 | * 247 | * @throws Error - If there is an error sending request to backend API or parsing response 248 | * 249 | * @returns Promise, what resolved to exchange rate or zero, if currencies pair not exists 250 | */ 251 | getExchangeRate( 252 | source: string, target: string, isForce: boolean = false, 253 | ): Promise { 254 | return this.getExchangeRates(isForce) 255 | .then((exchangeRates: ExchangeRates): string => { 256 | return getExchageRate(source, target, exchangeRates); 257 | }); 258 | } 259 | 260 | /** 261 | * Transfer 262 | * 263 | * Use {@link toTransfer} backend API result convert function and 264 | * prepare backend API parameters {@link prepareTransferOptions} function 265 | * 266 | * @param options - Transfer options 267 | * 268 | * @throws Error - If there is an error sending request to backend API, parsing response error 269 | * or options object is invalid 270 | * 271 | * @returns Promise, what resolved to completed transfer information object 272 | */ 273 | transfer(options: TransferOptions): Promise { 274 | return this._transport.call('transfer', prepareTransferOptions(options)) 275 | .then((result: any): Transfer => toTransfer(result)); 276 | } 277 | 278 | /** 279 | * Create invoice 280 | * 281 | * Use {@link toInvoice} backend API result convert function and 282 | * prepare backend API parameters {@link prepareCreateInvoiceOptions} function 283 | * 284 | * @param options - New invoice options 285 | * 286 | * @throws Error - If there is an error sending request to backend API, parsing response error 287 | * or options object is invalid 288 | * 289 | * @returns Promise, what resolved to created invoice information object 290 | */ 291 | createInvoice(options: CreateInvoiceOptions): Promise { 292 | return this._transport.call('createInvoice', prepareCreateInvoiceOptions(options)) 293 | .then((result: any): Invoice => toInvoice(result)); 294 | } 295 | 296 | /** 297 | * Create check 298 | * 299 | * Use {@link toCheck} backend API result convert function and 300 | * prepare backend API parameters {@link prepareCreateCheckOptions} function 301 | * 302 | * @param options - New check options 303 | * 304 | * @throws Error - If there is an error sending request to backend API, parsing response error 305 | * or options object is invalid 306 | * 307 | * @returns Promise, what resolved to created check information object 308 | */ 309 | createCheck(options: CreateCheckOptions): Promise { 310 | return this._transport.call('createCheck', prepareCreateCheckOptions(options)) 311 | .then((result: any): Check => toCheck(result)); 312 | } 313 | 314 | /** 315 | * Delete invoice 316 | * 317 | * @param id - Invoice identifier 318 | * 319 | * @throws Error - If there is an error sending request to backend API or parsing response error 320 | * 321 | * @returns Promise, what resolved to boolean operation result status 322 | */ 323 | deleteInvoice(id: number): Promise { 324 | return this._transport.call('deleteInvoice', { invoice_id: prepareDeleteOptions(id) }); 325 | } 326 | 327 | /** 328 | * Delete check 329 | * 330 | * @param id - Check identifier 331 | * 332 | * @throws Error - If there is an error sending request to backend API or parsing response error 333 | * 334 | * @returns Promise, what resolved to boolean operation result status 335 | */ 336 | deleteCheck(id: number): Promise { 337 | return this._transport.call('deleteCheck', { check_id: prepareDeleteOptions(id) }); 338 | } 339 | 340 | /** 341 | * Get invoices 342 | * 343 | * Use {@link toInvoices} backend API result convert function and 344 | * prepare backend API parameters {@link prepareGetInvoicesOptions} function 345 | * 346 | * @param options - Filters options 347 | * 348 | * @throws Error - If there is an error sending request to backend API or parsing response 349 | * 350 | * @returns Promise, what resolved to invoices information object 351 | */ 352 | getInvoices(options: GetInvoicesOptions = {}): Promise { 353 | return this._transport.call('getInvoices', prepareGetInvoicesOptions(options)) 354 | .then((result: any): Invoice[] => toInvoices(result)); 355 | } 356 | 357 | /** 358 | * Get invoices paginated 359 | * 360 | * Fetch invoices with `page` options parameter, except `count` and `offset` 361 | * 362 | * See {@link Client.getPageSize} and {@link Client.setPageSize} 363 | * 364 | * Use {@link toInvoices} backend API result convert function and 365 | * prepare backend API parameters {@link prepareGetInvoicesPaginateOptions} function 366 | * 367 | * @param options - Filters options 368 | * 369 | * @throws Error - If there is an error sending request to backend API, parsing response error 370 | * or options object is invalid 371 | * 372 | * @returns Promise, what resolved to invoices information object 373 | */ 374 | getInvoicesPaginate(options: GetInvoicesPaginateOptions = {}): Promise { 375 | const prepared = prepareGetInvoicesPaginateOptions(this._pageSize, options); 376 | 377 | return this._transport.call('getInvoices', prepared) 378 | .then((result: any): Invoice[] => toInvoices(result)); 379 | } 380 | 381 | /** 382 | * Get checks 383 | * 384 | * Use {@link toChecks} backend API result convert function and 385 | * prepare backend API parameters {@link prepareGetChecksOptions} function 386 | * 387 | * @param options - Filters options 388 | * 389 | * @throws Error - If there is an error sending request to backend API or parsing response 390 | * 391 | * @returns Promise, what resolved to checks information object 392 | */ 393 | getChecks(options: GetChecksOptions = {}): Promise { 394 | return this._transport.call('getChecks', prepareGetChecksOptions(options)) 395 | .then((result: any): Check[] => toChecks(result)); 396 | } 397 | 398 | /** 399 | * Get checks paginated 400 | * 401 | * Fetch checks with `page` options parameter, except `count` and `offset` 402 | * 403 | * See {@link Client.getPageSize} and {@link Client.setPageSize} 404 | * 405 | * Use {@link toChecks} backend API result convert function and 406 | * prepare backend API parameters {@link prepareGetChecksPaginateOptions} function 407 | * 408 | * @param options - Filters options 409 | * 410 | * @throws Error - If there is an error sending request to backend API, parsing response error 411 | * or options object is invalid 412 | * 413 | * @returns Promise, what resolved to checks information object 414 | */ 415 | getChecksPaginate(options: GetChecksPaginateOptions = {}): Promise { 416 | const prepared = prepareGetChecksPaginateOptions(this._pageSize, options); 417 | 418 | return this._transport.call('getChecks', prepared) 419 | .then((result: any): Check[] => toChecks(result)); 420 | } 421 | 422 | /** 423 | * Get transfers 424 | * 425 | * Use {@link toTransfers} backend API result convert function and 426 | * prepare backend API parameters {@link prepareGetTransfersOptions} function 427 | * 428 | * @param options - Filters options 429 | * 430 | * @throws Error - If there is an error sending request to backend API or parsing response 431 | * 432 | * @returns Promise, what resolved to transfers information object 433 | */ 434 | getTransfers(options: GetTransfersOptions = {}): Promise { 435 | return this._transport.call('getTransfers', prepareGetTransfersOptions(options)) 436 | .then((result: any): Transfer[] => toTransfers(result)); 437 | } 438 | 439 | /** 440 | * Get transfers paginated 441 | * 442 | * Fetch checks with `page` options parameter, except `count` and `offset` 443 | * 444 | * See {@link Client.getPageSize} and {@link Client.setPageSize} 445 | * 446 | * Use {@link toTransfers} backend API result convert function and 447 | * prepare backend API parameters {@link prepareGetTransfersOptions} function 448 | * 449 | * @param options - Filters options 450 | * 451 | * @throws Error - If there is an error sending request to backend API, parsing response error 452 | * or options object is invalid 453 | * 454 | * @returns Promise, what resolved to transfers information object 455 | */ 456 | getTransfersPaginate(options: GetTransfersPaginateOptions = {}): Promise { 457 | const prepared = prepareGetTransfersPaginateOptions(this._pageSize, options); 458 | 459 | return this._transport.call('getTransfers', prepared) 460 | .then((result: any): Transfer[] => toTransfers(result)); 461 | } 462 | 463 | /** 464 | * Call backend API method directly (types unsafe) 465 | * 466 | * Use it if backend API update (add new methods, change request or response fileds), 467 | * but library is not 468 | * 469 | * @param method - Backend API method name 470 | * @param options - Backend API options object 471 | * 472 | * @throws Error - If there is an error sending request to backend API or parsing response 473 | * 474 | * @returns Promise, what resolved to backend API response `result` field value 475 | */ 476 | call(method: string, options: object = {}): Promise { 477 | return this._transport.call(method as ApiMethod, options); 478 | } 479 | } 480 | -------------------------------------------------------------------------------- /src/classes/ClientEmitter.ts: -------------------------------------------------------------------------------- 1 | import { createHash, createHmac } from 'crypto'; 2 | import { ListenOptions } from 'net'; 3 | import { IncomingMessage, RequestListener, ServerResponse, Server, createServer } from 'http'; 4 | import { 5 | Server as SecureServer, ServerOptions, createServer as createSecureServer, 6 | } from 'https'; 7 | import Client from './Client'; 8 | import { Invoice, toInvoice } from '../helpers/casts'; 9 | import { Middleware } from '../helpers/utils'; 10 | 11 | /** 12 | * Check webhook data signature 13 | * 14 | * @param apiKey - Api key 15 | * @param signature - Webhook request signature 16 | * @param body - Webhook request body 17 | * 18 | * @returns Checking result 19 | */ 20 | export const checkSignature = (apiKey: string, signature: string, body: any): boolean => { 21 | try { 22 | const secret = createHash('sha256').update(apiKey).digest(); 23 | const checkString = JSON.stringify(body); 24 | const hmac = createHmac('sha256', secret).update(checkString).digest('hex'); 25 | return hmac === signature; 26 | } catch { 27 | return false; 28 | } 29 | }; 30 | 31 | /** 32 | * Read and parsing to JSON request body 33 | * 34 | * @param req - Node.js built-in IncomingMessage object 35 | * 36 | * @returns Promise, what resolved to parsed body or `null` for parsing error 37 | */ 38 | export const readRequestBody = ( 39 | req: IncomingMessage, 40 | ): Promise => new Promise((resolve): void => { 41 | let body: string = ''; 42 | 43 | req.on('data', (chunk: string): void => { 44 | body += chunk; 45 | }); 46 | 47 | req.on('end', () => { 48 | let data: any; 49 | try { 50 | data = JSON.parse(body); 51 | } catch { 52 | resolve(null); 53 | return; 54 | } 55 | 56 | resolve(data); 57 | }); 58 | }); 59 | 60 | // Because `tsdoc` not support `@category` tag, but `typedoc` support 61 | /* eslint-disable tsdoc/syntax */ 62 | /** 63 | * Main class for work with API for Node.js 64 | * 65 | * Library for Node.js default export this class 66 | * 67 | * @category External 68 | */ 69 | /* eslint-enable tsdoc/syntax */ 70 | class ClientEmitter extends Client { 71 | /** Api key */ 72 | private _apiKey: string; 73 | 74 | /** Handling webhooks created Node.js built-in server */ 75 | private _server: Server; 76 | 77 | /** Event listeners store */ 78 | private _events: { [key: string]: Array<(...args: any) => any> } = {}; 79 | 80 | // Because `tsdoc` throw `tsdoc-reference-selector-missing-parens`, 81 | // but `typedoc` doesn't recognize reference in parentheses 82 | /* eslint-disable tsdoc/syntax */ 83 | /** {@inheritDoc Client:constructor} */ 84 | /* eslint-enable tsdoc/syntax */ 85 | constructor(apiKey: string, endpoint: string = 'mainnet') { 86 | super(apiKey, endpoint); 87 | this._apiKey = apiKey; 88 | } 89 | 90 | /** 91 | * Create handling webhooks server 92 | * 93 | * If you app work behind proxy and no need create HTTPS server, 94 | * no pass `key` and `cert` fields and add `http` field with `true` value: `{ http: true }` 95 | * 96 | * Note: if you want to use self-signed certificate 97 | * you must uploat it in CryptoBot API application settings 98 | * 99 | * @param serverOptions - Node.js built-in server options 100 | * @param secretPath - Webhooks secret path, processing webhooks takes place only on it 101 | * @param listenOptions - Node.js built-in server listen options 102 | * 103 | * @throws Error - If create server error 104 | * 105 | * @returns Promise, what resolved `void` 106 | */ 107 | createServer( 108 | serverOptions: ServerOptions & { http?: boolean }, secretPath: string = '/', 109 | listenOptions: ListenOptions = { port: 443 }, 110 | ): Promise { 111 | return new Promise((resolve, reject) => { 112 | const requestListener: RequestListener = (req: IncomingMessage, res: ServerResponse) => { 113 | if (req.url !== secretPath) { 114 | res.statusCode = 404; 115 | res.end(); 116 | return; 117 | } 118 | 119 | readRequestBody(req).then((data: any): void => this._handleWebhook(data, req, res)); 120 | }; 121 | 122 | let server: Server | SecureServer; 123 | try { 124 | server = serverOptions.http === true 125 | ? createServer(serverOptions, requestListener) 126 | : createSecureServer(serverOptions, requestListener); 127 | } catch (err) { 128 | reject(err); 129 | return; 130 | } 131 | 132 | server.on('error', reject); 133 | 134 | try { 135 | server.listen(listenOptions, (): void => { 136 | this._server = server; 137 | resolve(); 138 | }); 139 | } catch (err) { 140 | reject(err); 141 | } 142 | }); 143 | } 144 | 145 | /** 146 | * Close created handling webhooks server 147 | * 148 | * @throws Error - If server not was started or closing error 149 | * 150 | * @returns Promise, what resolved `void` 151 | */ 152 | closeServer(): Promise { 153 | if (!this._server) return Promise.reject(new Error('Server not started')); 154 | 155 | return new Promise((resolve, reject) => { 156 | this._server.close((err?: Error): void => { 157 | if (err) reject(err); 158 | else resolve(); 159 | }); 160 | }); 161 | } 162 | 163 | /** 164 | * Create middleware function for Express.js-like API 165 | * 166 | * @returns Middleware function 167 | */ 168 | middleware(): Middleware { 169 | return (req: any, res: any): void => { 170 | Promise.resolve() 171 | .then((): any => req.body || readRequestBody(req)) 172 | .then((data: any): void => this._handleWebhook(data, req, res)); 173 | }; 174 | } 175 | 176 | /** 177 | * Subscribes to `paid` event 178 | * 179 | * See {@link ClientEmitter._emit} to more about event listener 180 | * 181 | * @param event - `paid` event name 182 | * @param listener - Event listener with `invoice` and `requestDate` callback parameters 183 | */ 184 | on(event: 'paid', listener: (invoice: Invoice, requestDate: Date) => any): void; 185 | 186 | /** 187 | * Subscribes to event 188 | * 189 | * @param event - Event name 190 | * @param listener - Event listener 191 | */ 192 | on(event: string, listener: (...args: any) => any): void { 193 | if (!this._events[event]) this._events[event] = []; 194 | this._events[event].push(listener); 195 | } 196 | 197 | /** 198 | * Unsubscribes from `paid` event 199 | * 200 | * @param event - `paid` event name 201 | * @param listener - Event listener with `invoice` and `requestDate` callback parameters 202 | */ 203 | off(event: 'paid', listener: (invoice: Invoice, requestDate: Date) => any): void; 204 | 205 | /** 206 | * Unsubscribes from event 207 | * 208 | * @param event - Event name 209 | * @param listener - Event listener 210 | */ 211 | off(event: string, listener: (...args: any) => any): void { 212 | if (!this._events[event]) return; 213 | 214 | const idx = this._events[event].indexOf(listener); 215 | if (idx > -1) this._events[event].splice(idx, 1); 216 | } 217 | 218 | /** 219 | * Emit event to listeners 220 | * 221 | * @param event - `paid` event name 222 | * @param invoice - Paid invoice information object 223 | * @param requestDate - Date of occurrence of event, need to filter old event. 224 | * If server is not available, backend API try resend webhooks by timeout, 225 | * so when server becomes available again, many old events 226 | * will be sent from backend API. 227 | */ 228 | private _emit(event: 'paid', invoice: Invoice, requestDate: Date): void; 229 | 230 | /** 231 | * Emit event to listeners 232 | * 233 | * @param event - Event name 234 | * @param params - Call event listeners parameters 235 | */ 236 | private _emit(event: string, ...params: any): void { 237 | if (!this._events[event]) return; 238 | 239 | this._events[event].forEach((listener: (...args: any) => any): void => { 240 | listener(...params); 241 | }); 242 | } 243 | 244 | /** 245 | * Handling webhook data, send response and emit events 246 | * 247 | * @param data - Parsed request body 248 | * @param req - Node.js built-in IncomingMessage object 249 | * @param res - Node.js built-in ServerResponse object 250 | */ 251 | private _handleWebhook(data: any, req: IncomingMessage, res: ServerResponse): void { 252 | if (!data) { 253 | res.statusCode = 401; 254 | res.end(); 255 | return; 256 | } 257 | 258 | const header = req.headers['crypto-pay-api-signature']; 259 | const signature = Array.isArray(header) ? header[0] : header; 260 | 261 | if (!checkSignature(this._apiKey, signature, data)) { 262 | res.statusCode = 401; 263 | res.end(); 264 | return; 265 | } 266 | 267 | if (data.update_type === 'invoice_paid') { 268 | this._emit( 269 | 'paid', toInvoice(data.payload), new Date(data.request_date), 270 | ); 271 | } 272 | 273 | res.end(); 274 | } 275 | } 276 | 277 | export default ClientEmitter; 278 | -------------------------------------------------------------------------------- /src/classes/Store.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Currencies, toCurrencies, 3 | ExchangeRates, toExchangeRates, 4 | Me, toMe, 5 | } from '../helpers/casts'; 6 | import { ApiMethod } from '../helpers/utils'; 7 | import Transport from './Transport'; 8 | 9 | /** 10 | * Create cached fetch handler for passed data type 11 | * 12 | * Store using for data types, which change infrequently. 13 | * For example, app infomation from `getMe` method. 14 | * For method calls more often than the passed update period, 15 | * returned data from cache without real request to backend API 16 | * 17 | * @remarks 18 | * Returned update data function receive `isForce` boolean parameter, 19 | * if it `true`, for this method call function makes real request to backend API 20 | * 21 | * @typeParam T - One of library methods return data type 22 | * 23 | * @param transport - Transport class instance 24 | * @param method - Backend API method, data type related 25 | * @param castFn - Convert backend API result to inner library method result type function 26 | * @param updatePeriod - Updatin data from backend API period 27 | * 28 | * @returns Update data type function 29 | */ 30 | export const createFetchHandler = ( 31 | transport: Transport, method: ApiMethod, castFn: (value: any) => T, updatePeriod: number, 32 | ): (isForce?: boolean) => Promise => { 33 | let promise: Promise = null; 34 | let prevUpdateStamp: number = 0; 35 | let data: T; 36 | 37 | return (isForce: boolean = false): Promise => { 38 | // If data fetching in process, return same promise 39 | // Need to prevent same multiple requests in one time 40 | // if Client class methods call parallel 41 | // This situation may arise due to the fact that some 42 | // methods need multiple backend API response to 43 | // return prepared result. For example, Client.getBalances 44 | // method need currencies information from getCurrencies 45 | // backend API method for correct formatting of amounts 46 | if (promise) return promise; 47 | 48 | // Calculate current update perion number 49 | const updateStamp = Math.floor(+(new Date()) / 1000 / updatePeriod); 50 | if (updateStamp === prevUpdateStamp && !isForce) { 51 | return Promise.resolve(data); 52 | } 53 | 54 | prevUpdateStamp = updateStamp; 55 | promise = transport.call(method).then((value: any): T => { 56 | data = castFn(value); 57 | promise = null; 58 | return data; 59 | }); 60 | 61 | return promise; 62 | }; 63 | }; 64 | 65 | // Because `tsdoc` not support `@category` tag, but `typedoc` support 66 | /* eslint-disable tsdoc/syntax */ 67 | /** 68 | * Wrapper for API methods that return possible cached data 69 | * 70 | * @category External 71 | */ 72 | /* eslint-enable tsdoc/syntax */ 73 | export default class Store { 74 | /** Update period for fetching currencies from backend API in seconds */ 75 | private static _CURRENCIES_UPDATE_PERIOD = 3600; 76 | 77 | /** Update period for fetching exhange rates from backend API in seconds */ 78 | private static _EXCHANGE_RATES_UPDATE_PERIOD = 60; 79 | 80 | /** Update period for fetching app infomation from backend API in seconds */ 81 | private static _ME_UPDATE_PERIOD = 3600; 82 | 83 | /** Transport class instance */ 84 | protected _transport: Transport; 85 | 86 | /** 87 | * {@link Store.getCurrencies} method fetch data handler, 88 | * see {@link createFetchHandler} for more 89 | */ 90 | private _currenciesFetchHandler: (isForce?: boolean) => Promise; 91 | 92 | /** 93 | * {@link Store.getExchangeRates} method fetch data handler, 94 | * see {@link createFetchHandler} for more 95 | */ 96 | private _exchangeRatesFetchHandler: (isForce?: boolean) => Promise; 97 | 98 | /** 99 | * {@link Store.getMe} method fetch data handler, 100 | * see {@link createFetchHandler} for more 101 | */ 102 | private _meFetchHandler: (isForce?: boolean) => Promise; 103 | 104 | /** 105 | * Create class instance 106 | * 107 | * @param apiKey - Crypto Bot API key, looks like '1234:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' 108 | * @param endpoint - API endpoint url or 'mainnet' or 'testnet' 109 | * for hardcoded in library endpoint urls 110 | * 111 | * @throws Error - If passed invalid API key or endpoint 112 | */ 113 | constructor(apiKey: string, endpoint: 'mainnet' | 'testnet' | string = 'mainnet') { 114 | this._transport = new Transport(apiKey, endpoint); 115 | } 116 | 117 | /** 118 | * Get API supported currencies infomation 119 | * 120 | * Use {@link toCurrencies} backend API result convert function 121 | * 122 | * @param isForce - If true, return fresh data from backend API, not from cache 123 | * 124 | * @throws Error - If there is an error sending request to backend API or parsing response 125 | * 126 | * @returns Promise, what resolved to API supported currencies infomation object 127 | */ 128 | getCurrencies(isForce?: boolean): Promise { 129 | if (!this._currenciesFetchHandler) { 130 | this._currenciesFetchHandler = createFetchHandler( 131 | this._transport, 'getCurrencies', toCurrencies, Store._CURRENCIES_UPDATE_PERIOD, 132 | ); 133 | } 134 | return this._currenciesFetchHandler(isForce); 135 | } 136 | 137 | /** 138 | * Get API supported currencies exchange rate infomation 139 | * 140 | * Use {@link toExchangeRates} backend API result convert function 141 | * 142 | * @param isForce - If true, return fresh data from backend API, not from cache 143 | * 144 | * @throws Error - If there is an error sending request to backend API or parsing response 145 | * 146 | * @returns Promise, what resolved to API supported currencies exchange rate infomation object 147 | */ 148 | getExchangeRates(isForce?: boolean): Promise { 149 | if (!this._exchangeRatesFetchHandler) { 150 | this._exchangeRatesFetchHandler = createFetchHandler( 151 | this._transport, 'getExchangeRates', toExchangeRates, Store._EXCHANGE_RATES_UPDATE_PERIOD, 152 | ); 153 | } 154 | return this._exchangeRatesFetchHandler(isForce); 155 | } 156 | 157 | /** 158 | * Get associated with passed API key app infomation 159 | * 160 | * Use {@link toMe} backend API result convert function 161 | * 162 | * @param isForce - If true, return fresh data from backend API, not from cache 163 | * 164 | * @throws Error - If there is an error sending request to backend API or parsing response 165 | * 166 | * @returns Promise, what resolved to associated with passed API key app infomation object 167 | */ 168 | getMe(isForce: boolean = false): Promise { 169 | if (!this._meFetchHandler) { 170 | this._meFetchHandler = createFetchHandler( 171 | this._transport, 'getMe', toMe, Store._ME_UPDATE_PERIOD, 172 | ); 173 | } 174 | return this._meFetchHandler(isForce); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/classes/Transport.ts: -------------------------------------------------------------------------------- 1 | import { ApiMethod, isValidUrl } from '../helpers/utils'; 2 | import request from '../request/http'; 3 | 4 | /** 5 | * Make backend API calls 6 | */ 7 | export default class Transport { 8 | /** RegExp to check API key */ 9 | static KEY_CHECK_REGEXP: RegExp = /\d{1,}:[a-zA-Z0-9]{35}/; 10 | 11 | /** Api key */ 12 | private _apiKey: string; 13 | 14 | /** Backend API endpoint base url */ 15 | private _baseUrl: string; 16 | 17 | /** 18 | * Create class instance 19 | * 20 | * @param apiKey - Crypto Bot API key, looks like '1234:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' 21 | * @param endpoint - API endpoint url or 'mainnet' or 'testnet' 22 | * for hardcoded in library endpoint urls 23 | * 24 | * @throws Error - If passed invalid API key or endpoint 25 | */ 26 | constructor(apiKey: string, endpoint: 'mainnet' | 'testnet' | string) { 27 | if (!Transport.KEY_CHECK_REGEXP.test(apiKey)) { 28 | throw new Error('API key looks like invalid'); 29 | } 30 | 31 | let url: string; 32 | 33 | if (endpoint === 'mainnet') { 34 | url = 'https://pay.crypt.bot/api'; 35 | } else if (endpoint === 'testnet') { 36 | url = 'https://testnet-pay.crypt.bot/api'; 37 | } else if (!isValidUrl(endpoint)) { 38 | throw new Error('Endpoint parameter not contain valid URL'); 39 | } else { 40 | url = endpoint; 41 | } 42 | 43 | this._apiKey = apiKey; 44 | this._baseUrl = `${url}/`; 45 | } 46 | 47 | /** 48 | * Make request to backend API, handle errors and return result 49 | * 50 | * @param method - Backend API method name 51 | * @param parameters - Method parameters object 52 | * 53 | * @throws Error - If response have errors 54 | * 55 | * @returns Promise, what resolved to API response `result` field 56 | */ 57 | call(method: ApiMethod, parameters?: object): Promise { 58 | // Format url query part from passed parameters object 59 | let qs = ''; 60 | if (parameters) { 61 | Object.keys(parameters).forEach((name): void => { 62 | let value = parameters[name]; 63 | 64 | if (Array.isArray(value)) value = value.join(','); 65 | else value = value.toString(); 66 | 67 | qs += `&${name}=${encodeURIComponent(value)}`; 68 | }); 69 | } 70 | 71 | return request(this._baseUrl + method + (qs.length ? `?${qs.substr(1)}` : ''), this._apiKey) 72 | .then((rawResponse: string): any => { 73 | let response: any; 74 | try { 75 | response = JSON.parse(rawResponse); 76 | } catch { 77 | throw new Error(`Response parse error, raw reponse:\n${rawResponse}`); 78 | } 79 | 80 | if (response.ok !== true) { 81 | if (!response.error) throw new Error('Api response unknown error'); 82 | throw new Error(`Api response error ${JSON.stringify(response.error)}`); 83 | } 84 | 85 | return response.result; 86 | }); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/helpers/casts.ts: -------------------------------------------------------------------------------- 1 | import { PaidBtnName } from './utils'; 2 | 3 | /** Result type value for {@link Client.getBalances} method */ 4 | export type Balance = { 5 | // Available balance 6 | available: string, 7 | // Balance on hold 8 | onhold: string, 9 | }; 10 | 11 | /** Result type for {@link Client.getBalances} method */ 12 | export type Balances = { [variant in CryptoCurrencyCode]: Balance }; 13 | 14 | /** Result type for {@link Client.getBalances} method */ 15 | export type BalancesType = { [variant in CryptoCurrencyCode]: string }; 16 | 17 | // Crypto currency codes 18 | export type CryptoCurrencyCode = 19 | 'USDT' | 'TON' | 'GRAM' | 'NOT' | 'MY' | 'DOGS' | 'BTC' | 'LTC' | 'ETH' | 'BNB' | 'TRX' | 'WIF' | 20 | 'USDC' | 'TRUMP' | 'MELANIA' | 'SOL' | 'DOGE' | 'PEPE' | 'BONK' | 'MAJOR' | 'HMSTR' | 'CATI' | 'MEMHASH'; 21 | 22 | // Fiat currency codes 23 | export type FiatCurrencyCode = 24 | 'USD' | 'EUR' | 'RUB' | 'BYN' | 'UAH' | 'GBP' | 'CNY' | 'KZT' | 'UZS' | 'GEL' | 'TRY' | 'AMD' | 25 | 'THB' | 'INR' | 'BRL' | 'IDR' | 'AZN' | 'AED' | 'PLN' | 'ILS' | 'KGS' | 'TJS'; 26 | 27 | // All curerencies codes 28 | export type CurrencyCode = CryptoCurrencyCode | FiatCurrencyCode; 29 | 30 | /** Possible currency types */ 31 | export enum CurrencyType { 32 | Crypto = 'crypto', 33 | Fiat = 'fiat', 34 | Unknown = 'unknown', 35 | } 36 | 37 | /** Possible detailed currency types */ 38 | export enum DetailedCurrencyType { 39 | Blockchain = 'blockchain', 40 | Stablecoin = 'stablecoin', 41 | Fiat = CurrencyType.Fiat, 42 | Unknown = CurrencyType.Unknown, 43 | } 44 | 45 | /** Possible invoice statuses */ 46 | export enum InvoiceStatus { 47 | Active = 'active', 48 | Paid = 'paid', 49 | Expired = 'expired', 50 | Unknown = 'unknown', 51 | } 52 | 53 | /** Possible check statuses */ 54 | export enum CheckStatus { 55 | Active = 'active', 56 | Activated = 'activated', 57 | Unknown = 'unknown', 58 | } 59 | 60 | /** Possible transfer statuses */ 61 | export enum TransferStatus { 62 | Completed = 'completed', 63 | Unknown = 'unknown', 64 | } 65 | 66 | /** 67 | * Currency type object for {@link Store.getCurrencies} 68 | * and {@link Client.getCurrency} methods results 69 | */ 70 | export type Currency = { 71 | /** Currency code */ 72 | code: CurrencyCode, 73 | /** Currency name */ 74 | name: string, 75 | /** Crypto currency office website url */ 76 | url?: string, 77 | /** Currency decimals count */ 78 | decimals: number, 79 | /** Currency type */ 80 | type: DetailedCurrencyType, 81 | }; 82 | 83 | /** Result type for {@link Store.getCurrencies} method */ 84 | export type Currencies = { [variant in CurrencyCode]?: Currency }; 85 | 86 | /** 87 | * Exchange rate type object for {@link Store.getExchangeRates} 88 | * and {@link Client.getExchangeRate} methods results 89 | */ 90 | export type ExchangeRate = { 91 | /** Source currency code */ 92 | source: CurrencyCode, 93 | /** Target currency code */ 94 | target: CurrencyCode, 95 | /** Source to target exchange rate */ 96 | rate: string, 97 | /** True, if the received rate is up-to-date */ 98 | isValid: boolean, 99 | }; 100 | 101 | /** Result type for {@link Store.getExchangeRates} method */ 102 | export type ExchangeRates = ExchangeRate[]; 103 | 104 | /** 105 | * Transfer type object for {@link Client.getTransfers} and {@link Client.transfer} methods results 106 | */ 107 | export type Transfer = { 108 | /** Transfer identifier */ 109 | id: number, 110 | /** 111 | * Transfer spend identifier, optional because not returned from `transfer` API method call 112 | */ 113 | spendId?: string, 114 | /** Telegram user ID the transfer was sent to */ 115 | userId: number, 116 | /** Transfer asset */ 117 | asset: CryptoCurrencyCode, 118 | /** Transfer amount */ 119 | amount: string, 120 | /** Transfer status */ 121 | status: TransferStatus, 122 | /** Transfer completed date */ 123 | completedAt: Date, 124 | /** Check activated date */ 125 | comment?: string, 126 | }; 127 | 128 | /** 129 | * Check type object for {@link Client.getChecks}, {@link Client.getChecksPaginate} 130 | * and {@link Client.createCheck} methods results 131 | */ 132 | export type Check = { 133 | /** Check identifier */ 134 | id: number, 135 | /** Check hash */ 136 | hash: string, 137 | /** Check asset */ 138 | asset: CryptoCurrencyCode, 139 | /** Check amount */ 140 | amount: string, 141 | /** Check receive url for user by bot */ 142 | botCheckUrl: string, 143 | /** Check status */ 144 | status: CheckStatus, 145 | /** Check created date */ 146 | createdAt: Date, 147 | /** Check activated date */ 148 | activatedAt?: Date, 149 | /** 150 | * ID of the user who will be able to activate the check, 151 | * only if passed in check creation, 152 | * if exists, field `pinToUsername` will be absent 153 | */ 154 | pinToUserId?: number, 155 | /** 156 | * A user with the specified username will be able to activate the check, 157 | * only if passed in check creation, 158 | * if exists, field `pinToUserId` will be absent 159 | */ 160 | pinToUsername?: string, 161 | }; 162 | 163 | /** 164 | * Invoice type object for {@link Client.getInvoices}, {@link Client.getInvoicesPaginate}, 165 | * {@link Client.createInvoice} methods results and {@link ClientEmitter} `paid` event emit 166 | */ 167 | export type Invoice = { 168 | /** Invoice identifier */ 169 | id: number, 170 | /** Invoice status */ 171 | status: InvoiceStatus, 172 | /** Invoice hash */ 173 | hash: string, 174 | /** Invoice currency type */ 175 | currencyType: CurrencyType, 176 | /** Invoice currency code */ 177 | currency: CurrencyCode, 178 | /** Invoice amount */ 179 | amount: string, 180 | /** Invoice pay url for user by bot */ 181 | botPayUrl: string, 182 | /** Invoice pay url for user by mini app */ 183 | miniAppPayUrl: string, 184 | /** Invoice pay url for user by web app */ 185 | webAppPayUrl: string, 186 | /** Is invoice allow user comment */ 187 | isAllowComments: boolean, 188 | /** Is user can pay invoice anonymously */ 189 | isAllowAnonymous: boolean, 190 | /** Invoice created date */ 191 | createdAt: Date, 192 | /** Text of the hidden message, only if set in invoice creation */ 193 | hiddenMessage?: string, 194 | /** Is invoice paid anonymously, only for paid invoice */ 195 | isPaidAnonymously?: boolean, 196 | /** Invoice paid date, only for paid invoice */ 197 | paidAt?: Date, 198 | /** Expiration date, only if set pay limit time in invoice creation */ 199 | expirationDate?: Date, 200 | /** Invoice displayed to user description, only if `description` passed in invoice creation */ 201 | description?: string, 202 | /** 203 | * Invoice visible only to app payload, only if `payload` passed in invoice creation 204 | * 205 | * If for invoice creation passed not string in this field, will be converted by JSON.parse 206 | */ 207 | payload?: any, 208 | /** 209 | * Invoice left user comment, only if set `isAllowComments` to true in invoice creation 210 | * and user left comment 211 | */ 212 | comment?: string, 213 | /** 214 | * Invoice displayed to user paid button name, 215 | * only if `paidBtnName` passed in invoice creation 216 | */ 217 | paidBtnName?: PaidBtnName, 218 | /** 219 | * Invoice displayed to user paid button url, 220 | * only if `paidBtnUrl` passed in invoice creation 221 | */ 222 | paidBtnUrl?: string, 223 | /** 224 | * Asset of service fees charged when the invoice was paid, only if status is InvoiceStatus.Paid 225 | */ 226 | feeAsset?: CryptoCurrencyCode, 227 | /** 228 | * Amount of service fees charged when the invoice was paid, only if status is InvoiceStatus.Paid 229 | */ 230 | fee?: number, 231 | /** 232 | * Price of the asset in USD, only if status is InvoiceStatus.Paid 233 | */ 234 | usdRate?: number, 235 | /** 236 | * List of assets which can be used to pay the invoice, only if set in invoice creation 237 | */ 238 | acceptedAssets?: CryptoCurrencyCode[], 239 | /** 240 | * Cryptocurrency alphabetic code for which the invoice was paid, 241 | * only if currency type is CurrencyType.Fiat and status is InvoiceStatus.Paid 242 | */ 243 | paidAsset?: CryptoCurrencyCode, 244 | /** 245 | * Amount of the invoice for which the invoice was paid, 246 | * only if currency type is CurrencyType.Fiat and status is InvoiceStatus.Paid 247 | */ 248 | paidAmount?: number, 249 | /** 250 | * The rate of the paid_asset valued in the fiat currency, 251 | * only if currency type is CurrencyType.Fiat and status is InvoiceStatus.Paid 252 | */ 253 | paidFiatRate?: number, 254 | }; 255 | 256 | /** Result type object for {@link Client.getStats} method */ 257 | export type Stats = { 258 | /** Total volume of paid invoices in USD */ 259 | volume: string, 260 | /** Conversion of all created invoices */ 261 | conversion: string, 262 | /** The unique number of users who have paid the invoice */ 263 | uniqueUsersCount: number, 264 | /** Total created invoice count */ 265 | createdInvoiceCount: number, 266 | /** Total paid invoice count */ 267 | paidInvoiceCount: number, 268 | /** The date on which the statistics calculation was started */ 269 | startAt: Date, 270 | /** The date on which the statistics calculation was ended */ 271 | endAt: Date, 272 | }; 273 | 274 | /** Result type object for {@link Store.getMe} method */ 275 | export type Me = { 276 | /** App identifier */ 277 | id: number, 278 | /** App name */ 279 | name: string, 280 | /** Using Telegram bot username */ 281 | bot: string, 282 | }; 283 | 284 | /** 285 | * Convert backend API result to library result object to return in 286 | * {@link Client.getBalances} method 287 | * 288 | * @param input - Backend API result 289 | * 290 | * @throws Error - If input parameter is not array 291 | * 292 | * @returns Converted result 293 | */ 294 | export const toBalances = (input: any): Balances => { 295 | if (!Array.isArray(input)) throw new Error(`Input is not array: ${JSON.stringify(input)}`); 296 | 297 | // Conver array to HashMap structure 298 | return input.reduce((accumulator: Balances, value: any): Balances => { 299 | accumulator[value.currency_code] = { available: value.available, onhold: value.onhold }; 300 | return accumulator; 301 | }, {} as Balances); 302 | }; 303 | 304 | /** 305 | * Convert backend API result to library result object to return in 306 | * {@link Store.getCurrencies} method 307 | * 308 | * @param input - Backend API result 309 | * 310 | * @returns Converted result 311 | */ 312 | export const toCurrencies = (input: any): Currencies => { 313 | if (!Array.isArray(input)) return {}; 314 | 315 | return input.reduce((accumulator: Currencies, value: any): Currencies => { 316 | if (value.code) { 317 | const code: CurrencyCode = value.code.toString(); 318 | 319 | let type = DetailedCurrencyType.Unknown; 320 | if (value.is_blockchain) type = DetailedCurrencyType.Blockchain; 321 | if (value.is_fiat) type = DetailedCurrencyType.Fiat; 322 | if (value.is_stablecoin) type = DetailedCurrencyType.Stablecoin; 323 | 324 | const currency: Currency = { 325 | code: code, 326 | name: value.name || '', 327 | decimals: value.decimals || 0, 328 | type, 329 | }; 330 | 331 | if (Object.prototype.hasOwnProperty.call(value, 'url')) currency.url = value.url; 332 | 333 | accumulator[code] = currency; 334 | } 335 | return accumulator; 336 | }, {}); 337 | }; 338 | 339 | /** 340 | * Convert backend API result to library result object to return in 341 | * {@link Store.getExchangeRates} method result 342 | * 343 | * @param input - Backend API result 344 | * 345 | * @returns Converted result 346 | */ 347 | export const toExchangeRates = (input: any): ExchangeRates => { 348 | if (!Array.isArray(input)) return []; 349 | 350 | return input.map((value: any): ExchangeRate => ({ 351 | source: value.source || '', 352 | target: value.target || '', 353 | rate: value.rate, 354 | isValid: value.is_valid, 355 | })); 356 | }; 357 | 358 | /** 359 | * Convert backend API result to library result object to return in 360 | * {@link Client.createInvoice} method, {@link toInvoices} function 361 | * and {@link ClientEmitter} `paid` event emit 362 | * 363 | * @param input - Backend API result 364 | * 365 | * @returns Converted result 366 | */ 367 | export const toInvoice = (input: any): Invoice => { 368 | const invoice: Invoice = { 369 | id: input.invoice_id || 0, 370 | status: input.status || InvoiceStatus.Unknown, 371 | hash: input.hash || '', 372 | currencyType: input.currency_type || '', 373 | currency: input.asset || input.fiat || '', 374 | amount: input.amount || '0', 375 | isAllowComments: input.allow_comments || false, 376 | isAllowAnonymous: input.allow_anonymous || false, 377 | createdAt: new Date(input.created_at), 378 | botPayUrl: input.bot_invoice_url || '', 379 | miniAppPayUrl: input.mini_app_invoice_url || '', 380 | webAppPayUrl: input.web_app_invoice_url || '', 381 | }; 382 | 383 | if (invoice.currencyType === CurrencyType.Crypto) { 384 | invoice.currency = input.asset || ''; 385 | } 386 | if (invoice.currencyType === CurrencyType.Fiat) { 387 | invoice.currency = input.fiat || ''; 388 | } 389 | 390 | if (input.hidden_message !== undefined) invoice.hiddenMessage = input.hidden_message; 391 | if (input.paid_anonymously !== undefined) invoice.isPaidAnonymously = input.paid_anonymously; 392 | if (input.expiration_date !== undefined) invoice.expirationDate = new Date(input.expiration_date); 393 | if (input.paid_at !== undefined) invoice.paidAt = new Date(input.paid_at); 394 | if (input.description !== undefined) invoice.description = input.description; 395 | if (input.paid_btn_name !== undefined) invoice.paidBtnName = input.paid_btn_name; 396 | if (input.paid_btn_url !== undefined) invoice.paidBtnUrl = input.paid_btn_url; 397 | if (input.comment !== undefined) invoice.comment = input.comment; 398 | if (input.paid_usd_rate !== undefined) invoice.usdRate = parseFloat(input.paid_usd_rate) || 0; 399 | if (input.fee_asset !== undefined) invoice.feeAsset = input.fee_asset || ''; 400 | if (input.fee_amount !== undefined) invoice.fee = input.fee_amount || 0; 401 | if (input.accepted_assets !== undefined) invoice.acceptedAssets = input.accepted_assets; 402 | if (input.paid_asset !== undefined) invoice.paidAsset = input.paid_asset || ''; 403 | if (input.paid_amount !== undefined) invoice.paidAmount = parseFloat(input.paid_amount) || 0; 404 | if (input.paid_fiat_rate !== undefined) invoice.paidFiatRate = parseFloat(input.paid_fiat_rate) || 0; 405 | if (input.payload !== undefined) { 406 | let payload: any; 407 | 408 | try { 409 | payload = JSON.parse(input.payload); 410 | } catch { 411 | payload = input.payload; 412 | } 413 | 414 | invoice.payload = payload; 415 | } 416 | 417 | return invoice; 418 | }; 419 | 420 | /** 421 | * Convert backend API result to library result object to return in 422 | * {@link Client.createCheck} method and {@link toChecks} function 423 | * 424 | * @param input - Backend API result 425 | * 426 | * @returns Converted result 427 | */ 428 | export const toCheck = (input: any): Check => { 429 | const check: Check = { 430 | id: input.check_id || 0, 431 | hash: input.hash || '', 432 | asset: input.asset || '', 433 | amount: input.amount || '0', 434 | botCheckUrl: input.bot_check_url || '', 435 | status: input.status || CheckStatus.Unknown, 436 | createdAt: new Date(input.created_at), 437 | }; 438 | 439 | if (input.activated_at !== undefined) check.activatedAt = new Date(input.activated_at); 440 | if (input.pin_to_user !== undefined && input.pin_to_user.pin_by !== undefined) { 441 | if (input.pin_to_user.pin_by === 'id' && input.pin_to_user.user_id !== undefined) { 442 | check.pinToUserId = input.pin_to_user.user_id; 443 | } 444 | if (input.pin_to_user.pin_by === 'username' && input.pin_to_user.username !== undefined) { 445 | check.pinToUsername = input.pin_to_user.username; 446 | } 447 | } 448 | 449 | return check; 450 | }; 451 | 452 | /** 453 | * Convert backend API result to library result object to return in 454 | * {@link Client.transfer} method and {@link toTransfers} function 455 | * 456 | * @param input - Backend API result 457 | * 458 | * @returns Converted result 459 | */ 460 | export const toTransfer = (input: any): Transfer => { 461 | const transfer: Transfer = { 462 | id: input.transfer_id || 0, 463 | userId: input.user_id || 0, 464 | asset: input.asset || '', 465 | amount: input.amount || '0', 466 | status: input.status || TransferStatus.Unknown, 467 | completedAt: new Date(input.completed_at), 468 | }; 469 | 470 | if (input.spend_id !== undefined) transfer.spendId = input.spend_id; 471 | if (input.comment !== undefined) transfer.comment = input.comment; 472 | 473 | return transfer; 474 | }; 475 | 476 | /** 477 | * Convert backend API result to library result object to return in 478 | * {@link Client.getInvoices} and {@link Client.getInvoicesPaginate} 479 | * methods 480 | * 481 | * @param input - Backend API result 482 | * 483 | * @returns Converted result 484 | */ 485 | export const toInvoices = (input: any): Invoice[] => { 486 | let items: Invoice[] = []; 487 | 488 | if (Array.isArray(input.items)) items = input.items.map(toInvoice); 489 | 490 | return items; 491 | }; 492 | 493 | /** 494 | * Convert backend API result to library result object to return in 495 | * {@link Client.getChecks} and {@link Client.getChecksPaginate} 496 | * methods 497 | * 498 | * @param input - Backend API result 499 | * 500 | * @returns Converted result 501 | */ 502 | export const toChecks = (input: any): Check[] => { 503 | let items: Check[] = []; 504 | 505 | if (Array.isArray(input.items)) items = input.items.map(toCheck); 506 | 507 | return items; 508 | }; 509 | 510 | /** 511 | * Convert backend API result to library result object to return in 512 | * {@link Client.getTransfers} and {@link Client.getTransfersPaginate} 513 | * methods 514 | * 515 | * @param input - Backend API result 516 | * 517 | * @returns Converted result 518 | */ 519 | export const toTransfers = (input: any): Transfer[] => { 520 | let items: Transfer[] = []; 521 | 522 | if (Array.isArray(input.items)) items = input.items.map(toTransfer); 523 | 524 | return items; 525 | }; 526 | 527 | /** 528 | * Convert backend API result to library result object to return in 529 | * {@link Client.getStats} method 530 | * 531 | * @param input - Backend API result 532 | * 533 | * @returns Converted result 534 | */ 535 | export const toStats = (input: any): Stats => ({ 536 | volume: input.volume || '0', 537 | conversion: input.conversion || '0', 538 | uniqueUsersCount: input.unique_users_count || 0, 539 | createdInvoiceCount: input.created_invoice_count || 0, 540 | paidInvoiceCount: input.paid_invoice_count || 0, 541 | startAt: new Date(input.start_at ? input.start_at : 0), 542 | endAt: new Date(input.end_at ? input.end_at : 0), 543 | }); 544 | 545 | /** 546 | * Convert backend API result to library result object to return in 547 | * {@link Store.getMe} method 548 | * 549 | * @param input - Backend API result 550 | * 551 | * @returns Converted result 552 | */ 553 | export const toMe = (input: any): Me => ({ 554 | id: input.app_id || 0, 555 | name: input.name || '', 556 | bot: input.payment_processing_bot_username || '', 557 | }); 558 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import Client from './classes/ClientEmitter'; 2 | 3 | export = Client; 4 | -------------------------------------------------------------------------------- /src/request/http.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage } from 'http'; 2 | import { request as httpsRequest, RequestOptions } from 'https'; 3 | 4 | /** 5 | * Make HTTP GET request 6 | * 7 | * Module imported for Node.js library building 8 | * 9 | * @param url - Url 10 | * @param apiKey - Crypto Bot API key 11 | * 12 | * @throws Error - If request fail 13 | * 14 | * @returns Raw response text 15 | */ 16 | const request = (url: string, apiKey: string): Promise => new Promise((resolve, reject) => { 17 | const options: RequestOptions = { 18 | headers: { 'Crypto-Pay-API-Token': apiKey }, 19 | }; 20 | 21 | const req = httpsRequest(url, options, (res: IncomingMessage): void => { 22 | let data = ''; 23 | 24 | res.on('error', reject); 25 | 26 | res.on('data', (chunk: string): void => { 27 | data += chunk; 28 | }); 29 | 30 | res.on('end', (): void => { 31 | resolve(data); 32 | }); 33 | }); 34 | 35 | req.on('error', reject); 36 | 37 | req.end(); 38 | }); 39 | 40 | export default request; 41 | -------------------------------------------------------------------------------- /src/request/xhr.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Make HTTP GET request 3 | * 4 | * Module imported only for browsers bundle 5 | * 6 | * @param url - Url 7 | * @param apiKey - Crypto Bot API key 8 | * 9 | * @throws Error - If request fail 10 | * 11 | * @returns Raw response text 12 | */ 13 | const request = (url: string, apiKey: string): Promise => new Promise((resolve, reject) => { 14 | const xhr = new XMLHttpRequest(); 15 | xhr.open('GET', url, true); 16 | xhr.setRequestHeader('Crypto-Pay-API-Token', apiKey); 17 | 18 | xhr.onreadystatechange = (): void => { 19 | if (xhr.readyState !== 4) return; 20 | resolve(xhr.responseText); 21 | }; 22 | 23 | xhr.onerror = (): void => { 24 | reject(new Error('Network Error')); 25 | }; 26 | 27 | xhr.send(); 28 | }); 29 | 30 | export default request; 31 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export { ListenOptions } from 'net'; 2 | export { ServerOptions as httpServerOptions } from 'http'; 3 | export { Server, ServerOptions } from 'https'; 4 | export { SecureContextOptions, TlsOptions } from 'tls'; 5 | export { default as Client } from './classes/Client'; 6 | export { default as ClientEmitter, checkSignature, readRequestBody } from './classes/ClientEmitter'; 7 | export { default as Store, createFetchHandler } from './classes/Store'; 8 | export { default as Transport } from './classes/Transport'; 9 | export * from './helpers/casts'; 10 | export * from './helpers/utils'; 11 | export { default as requestHttp } from './request/http'; 12 | export { default as requestXhr } from './request/xhr'; 13 | 14 | import ClientEmitter from './classes/ClientEmitter'; 15 | export default ClientEmitter; 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "target": "es2019", 5 | "declaration": true, 6 | "outDir": "./lib", 7 | "newLine": "lf" 8 | }, 9 | "include": [ 10 | "src/**/*" 11 | ], 12 | "typedocOptions": { 13 | "entryPoints": ["src/types.ts"], 14 | "out": "docs", 15 | "name": "Crypto Bot API", 16 | "hideGenerator": true, 17 | "githubPages": false, 18 | "sort": [ 19 | "static-first", 20 | "visibility", 21 | "required-first", 22 | "alphabetical" 23 | ], 24 | "defaultCategory": "Internal", 25 | "excludeExternals": true, 26 | "excludePrivate": false 27 | } 28 | } --------------------------------------------------------------------------------