├── .editorconfig ├── .github └── workflows │ └── npm-publish.yml ├── .gitignore ├── .mocharc.json ├── .npmignore ├── .travis.yml ├── README.md ├── examples └── example.ts ├── package.json ├── pnpm-lock.yaml ├── src ├── OpenNodeError.ts ├── client.ts ├── index.ts └── types │ ├── index.ts │ ├── v1.ts │ └── v2.ts ├── test └── index.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain 2 | # consistent coding styles between different editors and IDEs. 3 | 4 | root = true 5 | 6 | [*] 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Package & Publish 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-node@v3 13 | with: 14 | node-version: 16 15 | - run: npm test 16 | 17 | publish-npm: 18 | needs: build 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v3 22 | - uses: actions/setup-node@v3 23 | with: 24 | node-version: 16 25 | registry-url: https://registry.npmjs.org/ 26 | - run: npm publish 27 | env: 28 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Compiled binary addons (http://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Dependency directories 24 | node_modules 25 | jspm_packages 26 | 27 | # Optional npm cache directory 28 | .npm 29 | 30 | # Optional REPL history 31 | .node_repl_history 32 | 33 | # Editors 34 | .idea 35 | 36 | others 37 | .DS_Store 38 | 39 | # build dir 40 | dist 41 | 42 | # other lockfiles 43 | yarn.lock 44 | package-lock.json 45 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/mocharc", 3 | "timeout": 5000, 4 | "slow": 350, 5 | "bail": true, 6 | "reporter": "progress", 7 | "watch-files": [ 8 | "dist/**/*.js", 9 | "test/**/*.js" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | npm-debug.log* 3 | 4 | # Coverage directory used by tools like istanbul 5 | coverage 6 | .nyc_output 7 | 8 | # Dependency directories 9 | node_modules 10 | 11 | # package lock 12 | package-lock.json 13 | pnpm-lock.json 14 | yarn.lock 15 | 16 | # project files 17 | test 18 | examples 19 | CHANGELOG.md 20 | .babelrc 21 | .DS_Store 22 | .editorconf 23 | .editorconfig 24 | .eslintignore 25 | .eslintrc 26 | .gitignore 27 | .mocharc.json 28 | .travis.yml 29 | tsconfig.json 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8' 4 | script: 5 | - npm run test 6 | branches: 7 | only: 8 | - master 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenNode Node.js Library 2 | 3 | [![Version](https://img.shields.io/npm/v/opennode.svg)](https://www.npmjs.org/package/opennode) 4 | [![Build Status](https://travis-ci.org/opennodedev/opennode-node.svg?branch=master)](https://travis-ci.org/opennodedev/opennode-node) 5 | [![](https://badgen.net/npm/dt/opennode)](https://www.npmjs.com/package/opennode) 6 | [![Try opennode on RunKit](https://badge.runkitcdn.com/opennode.svg)](https://npm.runkit.com/opennode) 7 | 8 | The OpenNode Node library provides convenient access to the OpenNode API from 9 | applications written in server-side JavaScript. 10 | 11 | ## Documentation 12 | 13 | You can find examples [here](examples/example.js). For more information refer to our [API docs](https://opennode.co/docs). 14 | 15 | ## Installation 16 | 17 | Install the package with: 18 | 19 | npm install opennode --save 20 | or 21 | yarn add opennode 22 | 23 | ## Usage 24 | 25 | The package needs to be configured with your account's secret key which is 26 | available in your [OpenNode Dashboard](https://app.opennode.co/settings/api). 27 | value: 28 | 29 | ``` js 30 | const opennode = require('opennode'); 31 | opennode.setCredentials('MY_API_KEY', 'dev'); //if no parameter given, default environment is 'live' 32 | 33 | try { 34 | const charge = await opennode.createCharge({ 35 | amount: 10.5, 36 | currency: "USD", 37 | callback_url: "https://example.com/webhook/opennode", 38 | auto_settle: false 39 | }); 40 | } 41 | catch (error) { 42 | console.error(`${error.status} | ${error.message}`); 43 | } 44 | ``` 45 | 46 | ### Using Promises 47 | 48 | Every method returns a chainable promise which can be used instead of a regular 49 | callback: 50 | 51 | ```js 52 | // Create a new charge 53 | opennode.createCharge({ 54 | amount: 10.5, 55 | currency: "USD", 56 | callback_url: "https://example.com/webhook/opennode", 57 | auto_settle: false 58 | }).then(charge => { 59 | console.log(charge); 60 | }) 61 | .catch(error => { 62 | console.error(`${error.status} | ${error.message}`); 63 | }); 64 | ``` 65 | 66 | ### Webhook signing 67 | 68 | OpenNode can send signed webhook events that notify your application any time a specific event occurs. You can read more about it [here](https://developers.opennode.co/). 69 | 70 | You can verify if a webhook was sent by OpenNode by comparing the signatures. 71 | 72 | ```js 73 | 74 | function handleWebhook (req, res) { 75 | const charge = req.body; 76 | const isValid = await opennode.signatureIsValid(charge); 77 | 78 | if (isValid){ 79 | //Handle event 80 | } 81 | 82 | return res.sendStatus(200); 83 | } 84 | ``` 85 | -------------------------------------------------------------------------------- /examples/example.ts: -------------------------------------------------------------------------------- 1 | import * as opennode from "../src"; 2 | 3 | /** 4 | * Setup your API Key and environment 5 | */ 6 | opennode.setCredentials("MY_API_KEY", "dev"); 7 | 8 | /** 9 | * 10 | * Fetch charge information 11 | */ 12 | 13 | /** 14 | * Using promises 15 | */ 16 | 17 | opennode 18 | .chargeInfo("47bb5224-bf50-49d0-a317-4adfd345221a") 19 | .then((charge) => { 20 | console.log(charge); 21 | }) 22 | .catch((error) => { 23 | console.error(`${error.status} | ${error.message}`); 24 | }); 25 | 26 | /** 27 | * 28 | * Using async/await 29 | */ 30 | (async () => { 31 | try { 32 | const data = await opennode.chargeInfo( 33 | "47bb5224-bf50-49d0-a317-4adfd345221a" 34 | ); 35 | console.log(data); 36 | } catch (error) { 37 | console.error(`${error.status} | ${error.message}`); 38 | } 39 | })(); 40 | 41 | /** 42 | * 43 | * Creating a charge 44 | */ 45 | 46 | const charge = { 47 | amount: 10.5, 48 | currency: "USD", 49 | callback_url: "https://example.com/webhook/opennode", 50 | auto_settle: false, 51 | }; 52 | 53 | /** 54 | * Using promises 55 | */ 56 | 57 | opennode 58 | .createCharge(charge) 59 | .then((response) => { 60 | console.log(response); 61 | }) 62 | .catch((error) => { 63 | console.error(`${error.status} | ${error.message}`); 64 | }); 65 | 66 | /** 67 | * Using async/await 68 | */ 69 | 70 | (async () => { 71 | try { 72 | const response = await opennode.createCharge(charge); 73 | console.log(response); 74 | } catch (error) { 75 | console.error(`${error.status} | ${error.message}`); 76 | } 77 | })(); 78 | 79 | /** 80 | * 81 | * Generate a LNURL-Withdrawal 82 | */ 83 | 84 | /** 85 | * Using promises 86 | */ 87 | 88 | const withdrawal = { 89 | min_amt: 5000, 90 | max_amt: 5000, 91 | description: "Claim these 5000 sats", 92 | external_id: "my-external-uuid", 93 | callback_url: "https://example.com/webhook/opennode", 94 | }; 95 | 96 | opennode 97 | .createLnUrlWithdrawal(withdrawal) 98 | .then((response) => { 99 | console.log(response); 100 | }) 101 | .catch((error) => { 102 | console.error(`${error.status} | ${error.message}`); 103 | }); 104 | 105 | /** 106 | * Using async/await 107 | */ 108 | 109 | (async () => { 110 | try { 111 | const response = await opennode.createLnUrlWithdrawal(withdrawal); 112 | console.log(response); 113 | } catch (error) { 114 | console.error(`${error.status} | ${error.message}`); 115 | } 116 | })(); 117 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opennode", 3 | "description": "Node.js library for the OpenNode API.", 4 | "homepage": "https://developers.opennode.com/", 5 | "bugs": "https://github.com/opennodedev/opennode-node/issues", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/opennodedev/opennode-node" 9 | }, 10 | "author": { 11 | "name": "OpenNode Inc", 12 | "email": "support@opennode.com", 13 | "url": "https://opennode.com" 14 | }, 15 | "contributors": [ 16 | "João Almeida " 17 | ], 18 | "license": "MIT", 19 | "keywords": [ 20 | "bitcoin", 21 | "lightning network", 22 | "ln", 23 | "microtransactions", 24 | "payment processing", 25 | "payment gateway", 26 | "api", 27 | "opennode" 28 | ], 29 | "main": "dist/index.js", 30 | "version": "1.5.0", 31 | "dependencies": { 32 | "axios": "^1.3.4" 33 | }, 34 | "devDependencies": { 35 | "@types/node": "^18.15.3", 36 | "chai": "^4.3.6", 37 | "mocha": "^9.2.2", 38 | "typescript": "^4.9.5" 39 | }, 40 | "scripts": { 41 | "test": "pnpm build && mocha", 42 | "build": "rm -rf dist/ && tsc", 43 | "prepack": "pnpm build" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | axios: 9 | specifier: ^1.3.4 10 | version: 1.3.4 11 | 12 | devDependencies: 13 | '@types/node': 14 | specifier: ^18.15.3 15 | version: 18.15.3 16 | chai: 17 | specifier: ^4.3.6 18 | version: 4.3.7 19 | mocha: 20 | specifier: ^9.2.2 21 | version: 9.2.2 22 | typescript: 23 | specifier: ^4.9.5 24 | version: 4.9.5 25 | 26 | packages: 27 | 28 | /@types/node@18.15.3: 29 | resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==} 30 | dev: true 31 | 32 | /@ungap/promise-all-settled@1.1.2: 33 | resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} 34 | dev: true 35 | 36 | /ansi-colors@4.1.1: 37 | resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} 38 | engines: {node: '>=6'} 39 | dev: true 40 | 41 | /ansi-regex@5.0.1: 42 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 43 | engines: {node: '>=8'} 44 | dev: true 45 | 46 | /ansi-styles@4.3.0: 47 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 48 | engines: {node: '>=8'} 49 | dependencies: 50 | color-convert: 2.0.1 51 | dev: true 52 | 53 | /anymatch@3.1.3: 54 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 55 | engines: {node: '>= 8'} 56 | dependencies: 57 | normalize-path: 3.0.0 58 | picomatch: 2.3.1 59 | dev: true 60 | 61 | /argparse@2.0.1: 62 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 63 | dev: true 64 | 65 | /assertion-error@1.1.0: 66 | resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} 67 | dev: true 68 | 69 | /asynckit@0.4.0: 70 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} 71 | dev: false 72 | 73 | /axios@1.3.4: 74 | resolution: {integrity: sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==} 75 | dependencies: 76 | follow-redirects: 1.15.4 77 | form-data: 4.0.0 78 | proxy-from-env: 1.1.0 79 | transitivePeerDependencies: 80 | - debug 81 | dev: false 82 | 83 | /balanced-match@1.0.2: 84 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 85 | dev: true 86 | 87 | /binary-extensions@2.2.0: 88 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 89 | engines: {node: '>=8'} 90 | dev: true 91 | 92 | /brace-expansion@1.1.11: 93 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 94 | dependencies: 95 | balanced-match: 1.0.2 96 | concat-map: 0.0.1 97 | dev: true 98 | 99 | /braces@3.0.2: 100 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 101 | engines: {node: '>=8'} 102 | dependencies: 103 | fill-range: 7.0.1 104 | dev: true 105 | 106 | /browser-stdout@1.3.1: 107 | resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} 108 | dev: true 109 | 110 | /camelcase@6.3.0: 111 | resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} 112 | engines: {node: '>=10'} 113 | dev: true 114 | 115 | /chai@4.3.7: 116 | resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} 117 | engines: {node: '>=4'} 118 | dependencies: 119 | assertion-error: 1.1.0 120 | check-error: 1.0.2 121 | deep-eql: 4.1.3 122 | get-func-name: 2.0.0 123 | loupe: 2.3.6 124 | pathval: 1.1.1 125 | type-detect: 4.0.8 126 | dev: true 127 | 128 | /chalk@4.1.2: 129 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 130 | engines: {node: '>=10'} 131 | dependencies: 132 | ansi-styles: 4.3.0 133 | supports-color: 7.2.0 134 | dev: true 135 | 136 | /check-error@1.0.2: 137 | resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} 138 | dev: true 139 | 140 | /chokidar@3.5.3: 141 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 142 | engines: {node: '>= 8.10.0'} 143 | dependencies: 144 | anymatch: 3.1.3 145 | braces: 3.0.2 146 | glob-parent: 5.1.2 147 | is-binary-path: 2.1.0 148 | is-glob: 4.0.3 149 | normalize-path: 3.0.0 150 | readdirp: 3.6.0 151 | optionalDependencies: 152 | fsevents: 2.3.2 153 | dev: true 154 | 155 | /cliui@7.0.4: 156 | resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} 157 | dependencies: 158 | string-width: 4.2.3 159 | strip-ansi: 6.0.1 160 | wrap-ansi: 7.0.0 161 | dev: true 162 | 163 | /color-convert@2.0.1: 164 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 165 | engines: {node: '>=7.0.0'} 166 | dependencies: 167 | color-name: 1.1.4 168 | dev: true 169 | 170 | /color-name@1.1.4: 171 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 172 | dev: true 173 | 174 | /combined-stream@1.0.8: 175 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 176 | engines: {node: '>= 0.8'} 177 | dependencies: 178 | delayed-stream: 1.0.0 179 | dev: false 180 | 181 | /concat-map@0.0.1: 182 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 183 | dev: true 184 | 185 | /debug@4.3.3(supports-color@8.1.1): 186 | resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} 187 | engines: {node: '>=6.0'} 188 | peerDependencies: 189 | supports-color: '*' 190 | peerDependenciesMeta: 191 | supports-color: 192 | optional: true 193 | dependencies: 194 | ms: 2.1.2 195 | supports-color: 8.1.1 196 | dev: true 197 | 198 | /decamelize@4.0.0: 199 | resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} 200 | engines: {node: '>=10'} 201 | dev: true 202 | 203 | /deep-eql@4.1.3: 204 | resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} 205 | engines: {node: '>=6'} 206 | dependencies: 207 | type-detect: 4.0.8 208 | dev: true 209 | 210 | /delayed-stream@1.0.0: 211 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} 212 | engines: {node: '>=0.4.0'} 213 | dev: false 214 | 215 | /diff@5.0.0: 216 | resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} 217 | engines: {node: '>=0.3.1'} 218 | dev: true 219 | 220 | /emoji-regex@8.0.0: 221 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 222 | dev: true 223 | 224 | /escalade@3.1.1: 225 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} 226 | engines: {node: '>=6'} 227 | dev: true 228 | 229 | /escape-string-regexp@4.0.0: 230 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 231 | engines: {node: '>=10'} 232 | dev: true 233 | 234 | /fill-range@7.0.1: 235 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 236 | engines: {node: '>=8'} 237 | dependencies: 238 | to-regex-range: 5.0.1 239 | dev: true 240 | 241 | /find-up@5.0.0: 242 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 243 | engines: {node: '>=10'} 244 | dependencies: 245 | locate-path: 6.0.0 246 | path-exists: 4.0.0 247 | dev: true 248 | 249 | /flat@5.0.2: 250 | resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} 251 | hasBin: true 252 | dev: true 253 | 254 | /follow-redirects@1.15.4: 255 | resolution: {integrity: sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==} 256 | engines: {node: '>=4.0'} 257 | peerDependencies: 258 | debug: '*' 259 | peerDependenciesMeta: 260 | debug: 261 | optional: true 262 | dev: false 263 | 264 | /form-data@4.0.0: 265 | resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} 266 | engines: {node: '>= 6'} 267 | dependencies: 268 | asynckit: 0.4.0 269 | combined-stream: 1.0.8 270 | mime-types: 2.1.35 271 | dev: false 272 | 273 | /fs.realpath@1.0.0: 274 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 275 | dev: true 276 | 277 | /fsevents@2.3.2: 278 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 279 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 280 | os: [darwin] 281 | requiresBuild: true 282 | dev: true 283 | optional: true 284 | 285 | /get-caller-file@2.0.5: 286 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 287 | engines: {node: 6.* || 8.* || >= 10.*} 288 | dev: true 289 | 290 | /get-func-name@2.0.0: 291 | resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} 292 | dev: true 293 | 294 | /glob-parent@5.1.2: 295 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 296 | engines: {node: '>= 6'} 297 | dependencies: 298 | is-glob: 4.0.3 299 | dev: true 300 | 301 | /glob@7.2.0: 302 | resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} 303 | dependencies: 304 | fs.realpath: 1.0.0 305 | inflight: 1.0.6 306 | inherits: 2.0.4 307 | minimatch: 3.1.2 308 | once: 1.4.0 309 | path-is-absolute: 1.0.1 310 | dev: true 311 | 312 | /growl@1.10.5: 313 | resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==} 314 | engines: {node: '>=4.x'} 315 | dev: true 316 | 317 | /has-flag@4.0.0: 318 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 319 | engines: {node: '>=8'} 320 | dev: true 321 | 322 | /he@1.2.0: 323 | resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} 324 | hasBin: true 325 | dev: true 326 | 327 | /inflight@1.0.6: 328 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 329 | dependencies: 330 | once: 1.4.0 331 | wrappy: 1.0.2 332 | dev: true 333 | 334 | /inherits@2.0.4: 335 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 336 | dev: true 337 | 338 | /is-binary-path@2.1.0: 339 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 340 | engines: {node: '>=8'} 341 | dependencies: 342 | binary-extensions: 2.2.0 343 | dev: true 344 | 345 | /is-extglob@2.1.1: 346 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 347 | engines: {node: '>=0.10.0'} 348 | dev: true 349 | 350 | /is-fullwidth-code-point@3.0.0: 351 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 352 | engines: {node: '>=8'} 353 | dev: true 354 | 355 | /is-glob@4.0.3: 356 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 357 | engines: {node: '>=0.10.0'} 358 | dependencies: 359 | is-extglob: 2.1.1 360 | dev: true 361 | 362 | /is-number@7.0.0: 363 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 364 | engines: {node: '>=0.12.0'} 365 | dev: true 366 | 367 | /is-plain-obj@2.1.0: 368 | resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} 369 | engines: {node: '>=8'} 370 | dev: true 371 | 372 | /is-unicode-supported@0.1.0: 373 | resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} 374 | engines: {node: '>=10'} 375 | dev: true 376 | 377 | /isexe@2.0.0: 378 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 379 | dev: true 380 | 381 | /js-yaml@4.1.0: 382 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 383 | hasBin: true 384 | dependencies: 385 | argparse: 2.0.1 386 | dev: true 387 | 388 | /locate-path@6.0.0: 389 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 390 | engines: {node: '>=10'} 391 | dependencies: 392 | p-locate: 5.0.0 393 | dev: true 394 | 395 | /log-symbols@4.1.0: 396 | resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} 397 | engines: {node: '>=10'} 398 | dependencies: 399 | chalk: 4.1.2 400 | is-unicode-supported: 0.1.0 401 | dev: true 402 | 403 | /loupe@2.3.6: 404 | resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} 405 | dependencies: 406 | get-func-name: 2.0.0 407 | dev: true 408 | 409 | /mime-db@1.52.0: 410 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 411 | engines: {node: '>= 0.6'} 412 | dev: false 413 | 414 | /mime-types@2.1.35: 415 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 416 | engines: {node: '>= 0.6'} 417 | dependencies: 418 | mime-db: 1.52.0 419 | dev: false 420 | 421 | /minimatch@3.1.2: 422 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 423 | dependencies: 424 | brace-expansion: 1.1.11 425 | dev: true 426 | 427 | /minimatch@4.2.1: 428 | resolution: {integrity: sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==} 429 | engines: {node: '>=10'} 430 | dependencies: 431 | brace-expansion: 1.1.11 432 | dev: true 433 | 434 | /mocha@9.2.2: 435 | resolution: {integrity: sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==} 436 | engines: {node: '>= 12.0.0'} 437 | hasBin: true 438 | dependencies: 439 | '@ungap/promise-all-settled': 1.1.2 440 | ansi-colors: 4.1.1 441 | browser-stdout: 1.3.1 442 | chokidar: 3.5.3 443 | debug: 4.3.3(supports-color@8.1.1) 444 | diff: 5.0.0 445 | escape-string-regexp: 4.0.0 446 | find-up: 5.0.0 447 | glob: 7.2.0 448 | growl: 1.10.5 449 | he: 1.2.0 450 | js-yaml: 4.1.0 451 | log-symbols: 4.1.0 452 | minimatch: 4.2.1 453 | ms: 2.1.3 454 | nanoid: 3.3.1 455 | serialize-javascript: 6.0.0 456 | strip-json-comments: 3.1.1 457 | supports-color: 8.1.1 458 | which: 2.0.2 459 | workerpool: 6.2.0 460 | yargs: 16.2.0 461 | yargs-parser: 20.2.4 462 | yargs-unparser: 2.0.0 463 | dev: true 464 | 465 | /ms@2.1.2: 466 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 467 | dev: true 468 | 469 | /ms@2.1.3: 470 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 471 | dev: true 472 | 473 | /nanoid@3.3.1: 474 | resolution: {integrity: sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==} 475 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 476 | hasBin: true 477 | dev: true 478 | 479 | /normalize-path@3.0.0: 480 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 481 | engines: {node: '>=0.10.0'} 482 | dev: true 483 | 484 | /once@1.4.0: 485 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 486 | dependencies: 487 | wrappy: 1.0.2 488 | dev: true 489 | 490 | /p-limit@3.1.0: 491 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 492 | engines: {node: '>=10'} 493 | dependencies: 494 | yocto-queue: 0.1.0 495 | dev: true 496 | 497 | /p-locate@5.0.0: 498 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 499 | engines: {node: '>=10'} 500 | dependencies: 501 | p-limit: 3.1.0 502 | dev: true 503 | 504 | /path-exists@4.0.0: 505 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 506 | engines: {node: '>=8'} 507 | dev: true 508 | 509 | /path-is-absolute@1.0.1: 510 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 511 | engines: {node: '>=0.10.0'} 512 | dev: true 513 | 514 | /pathval@1.1.1: 515 | resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} 516 | dev: true 517 | 518 | /picomatch@2.3.1: 519 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 520 | engines: {node: '>=8.6'} 521 | dev: true 522 | 523 | /proxy-from-env@1.1.0: 524 | resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} 525 | dev: false 526 | 527 | /randombytes@2.1.0: 528 | resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} 529 | dependencies: 530 | safe-buffer: 5.2.1 531 | dev: true 532 | 533 | /readdirp@3.6.0: 534 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 535 | engines: {node: '>=8.10.0'} 536 | dependencies: 537 | picomatch: 2.3.1 538 | dev: true 539 | 540 | /require-directory@2.1.1: 541 | resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} 542 | engines: {node: '>=0.10.0'} 543 | dev: true 544 | 545 | /safe-buffer@5.2.1: 546 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 547 | dev: true 548 | 549 | /serialize-javascript@6.0.0: 550 | resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} 551 | dependencies: 552 | randombytes: 2.1.0 553 | dev: true 554 | 555 | /string-width@4.2.3: 556 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 557 | engines: {node: '>=8'} 558 | dependencies: 559 | emoji-regex: 8.0.0 560 | is-fullwidth-code-point: 3.0.0 561 | strip-ansi: 6.0.1 562 | dev: true 563 | 564 | /strip-ansi@6.0.1: 565 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 566 | engines: {node: '>=8'} 567 | dependencies: 568 | ansi-regex: 5.0.1 569 | dev: true 570 | 571 | /strip-json-comments@3.1.1: 572 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 573 | engines: {node: '>=8'} 574 | dev: true 575 | 576 | /supports-color@7.2.0: 577 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 578 | engines: {node: '>=8'} 579 | dependencies: 580 | has-flag: 4.0.0 581 | dev: true 582 | 583 | /supports-color@8.1.1: 584 | resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} 585 | engines: {node: '>=10'} 586 | dependencies: 587 | has-flag: 4.0.0 588 | dev: true 589 | 590 | /to-regex-range@5.0.1: 591 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 592 | engines: {node: '>=8.0'} 593 | dependencies: 594 | is-number: 7.0.0 595 | dev: true 596 | 597 | /type-detect@4.0.8: 598 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} 599 | engines: {node: '>=4'} 600 | dev: true 601 | 602 | /typescript@4.9.5: 603 | resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} 604 | engines: {node: '>=4.2.0'} 605 | hasBin: true 606 | dev: true 607 | 608 | /which@2.0.2: 609 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 610 | engines: {node: '>= 8'} 611 | hasBin: true 612 | dependencies: 613 | isexe: 2.0.0 614 | dev: true 615 | 616 | /workerpool@6.2.0: 617 | resolution: {integrity: sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==} 618 | dev: true 619 | 620 | /wrap-ansi@7.0.0: 621 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 622 | engines: {node: '>=10'} 623 | dependencies: 624 | ansi-styles: 4.3.0 625 | string-width: 4.2.3 626 | strip-ansi: 6.0.1 627 | dev: true 628 | 629 | /wrappy@1.0.2: 630 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 631 | dev: true 632 | 633 | /y18n@5.0.8: 634 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 635 | engines: {node: '>=10'} 636 | dev: true 637 | 638 | /yargs-parser@20.2.4: 639 | resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} 640 | engines: {node: '>=10'} 641 | dev: true 642 | 643 | /yargs-unparser@2.0.0: 644 | resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} 645 | engines: {node: '>=10'} 646 | dependencies: 647 | camelcase: 6.3.0 648 | decamelize: 4.0.0 649 | flat: 5.0.2 650 | is-plain-obj: 2.1.0 651 | dev: true 652 | 653 | /yargs@16.2.0: 654 | resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} 655 | engines: {node: '>=10'} 656 | dependencies: 657 | cliui: 7.0.4 658 | escalade: 3.1.1 659 | get-caller-file: 2.0.5 660 | require-directory: 2.1.1 661 | string-width: 4.2.3 662 | y18n: 5.0.8 663 | yargs-parser: 20.2.4 664 | dev: true 665 | 666 | /yocto-queue@0.1.0: 667 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 668 | engines: {node: '>=10'} 669 | dev: true 670 | -------------------------------------------------------------------------------- /src/OpenNodeError.ts: -------------------------------------------------------------------------------- 1 | export class OpenNodeError extends Error { 2 | constructor( 3 | message: string | undefined, 4 | public name: string = "unknown", 5 | public status?: number 6 | ) { 7 | super(message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/client.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance } from "axios"; 2 | import { createHmac } from "node:crypto"; 3 | import { deprecate } from "node:util"; 4 | import { OpenNodeError } from "./OpenNodeError"; 5 | import { OpenNodeEnv } from "./types"; 6 | import { 7 | OpenNodeBalance, 8 | OpenNodeCharge, 9 | OpenNodeChargeRequest, 10 | OpenNodeChargeWebhook, 11 | OpenNodeRates, 12 | OpenNodeRefund, 13 | OpenNodeRefundRequest, 14 | OpenNodeWithdrawal, 15 | OpenNodeWithdrawalRequest, 16 | } from "./types/v1"; 17 | import * as v2 from "./types/v2"; 18 | 19 | const version = require("../package.json")?.version || "local"; 20 | const packageVersion = `npm-opennode-v${version}`; 21 | 22 | export class OpenNodeClient { 23 | public env: OpenNodeEnv; 24 | 25 | private api_key: string; 26 | private instanceV1: AxiosInstance; 27 | private instanceV2: AxiosInstance; 28 | 29 | constructor(key: string, environment: OpenNodeEnv = "live") { 30 | this.api_key = key; 31 | this.env = environment; 32 | 33 | const createInstance = (version: string): AxiosInstance => { 34 | const client = axios.create({ 35 | baseURL: 36 | environment === "live" 37 | ? `https://api.opennode.com/${version}` 38 | : `https://dev-api.opennode.com/${version}`, 39 | timeout: 15000, 40 | headers: { 41 | Authorization: this.api_key, 42 | Connection: "Keep-Alive", 43 | "Content-Type": "application/json", 44 | "Keep-Alive": "timeout=10", 45 | "User-Agent": packageVersion, 46 | }, 47 | }); 48 | 49 | client.interceptors.response.use( 50 | // normalize responses 51 | ({ data }) => ("data" in data ? data.data : data), 52 | (err) => { 53 | if (axios.isAxiosError(err)) { 54 | // added to keep compatibility with previous versions 55 | throw new OpenNodeError( 56 | err.message, 57 | err.response?.statusText, 58 | err.response?.status 59 | ); 60 | } 61 | 62 | if (err instanceof Error) throw err; 63 | 64 | return err; 65 | } 66 | ); 67 | 68 | return client; 69 | }; 70 | 71 | this.instanceV1 = createInstance("v1"); 72 | this.instanceV2 = createInstance("v2"); 73 | } 74 | 75 | async createCharge(charge: OpenNodeChargeRequest): Promise { 76 | return this.instanceV1.post(`/charges`, charge); 77 | } 78 | 79 | async chargeInfo(id: string): Promise { 80 | return this.instanceV1.get(`/charge/${id}`); 81 | } 82 | 83 | async listCharges(): Promise { 84 | return this.instanceV1.get(`/charges`); 85 | } 86 | 87 | initiateWithdrawal = deprecate( 88 | (withdrawal: OpenNodeWithdrawalRequest): Promise => { 89 | return this.instanceV1.post(`/withdrawals`, withdrawal); 90 | }, 91 | "This method is deprecated and not recommend for use. Please use the asynchronous version (initiateWithdrawalAsync)", 92 | "OPN001" 93 | ); 94 | 95 | async withdrawalInfo(id: string): Promise { 96 | return this.instanceV1.get(`/withdrawal/${id}`); 97 | } 98 | 99 | async listWithdrawals(): Promise { 100 | return this.instanceV1.get(`/withdrawals`); 101 | } 102 | 103 | async listRates(): Promise { 104 | return this.instanceV1.get(`/rates`); 105 | } 106 | 107 | async listCurrencies(): Promise { 108 | return this.instanceV1.get(`/currencies`); 109 | } 110 | 111 | /** @deprecated Use {@link accountBalance} */ 112 | async getBalance() { 113 | return this.accountBalance(); 114 | } 115 | 116 | /** @deprecated Use {@link accountBalance} */ 117 | async userBalance() { 118 | return this.accountBalance(); 119 | } 120 | 121 | async accountBalance(): Promise { 122 | return this.instanceV1.get(`/account/balance`); 123 | } 124 | 125 | verifySignature(charge: OpenNodeChargeWebhook): boolean { 126 | const hash = createHmac("sha256", this.api_key) 127 | .update(charge.id) 128 | .digest("hex"); 129 | return hash === charge.hashed_order; 130 | } 131 | 132 | async refundCharge(refund: OpenNodeRefundRequest): Promise { 133 | return this.instanceV1.post(`/refunds`, refund); 134 | } 135 | 136 | async listRefunds(): Promise { 137 | return this.instanceV1.get(`/refunds`); 138 | } 139 | 140 | async refundInfo(id: string): Promise { 141 | return this.instanceV1.get(`/refund/${id}`); 142 | } 143 | 144 | async initiatePayout( 145 | payout: v2.OpenNodePayoutRequest 146 | ): Promise { 147 | return this.instanceV2.post(`/payouts`, payout); 148 | } 149 | 150 | async payoutInfo(id: string): Promise { 151 | return this.instanceV2.get(`/payout/${id}`); 152 | } 153 | 154 | async initiateExchange( 155 | exchange: v2.OpenNodeExchangeRequest 156 | ): Promise { 157 | return this.instanceV2.post("/exchanges", exchange); 158 | } 159 | 160 | async initiateWithdrawalAsync( 161 | withdrawal: v2.OpenNodeWithdrawalRequest 162 | ): Promise { 163 | return this.instanceV2.post("/withdrawals", withdrawal); 164 | } 165 | 166 | async createLnUrlWithdrawal( 167 | lnUrlWithdrawal: v2.OpenNodeLnURLWithdrawalRequest 168 | ): Promise { 169 | return this.instanceV2.post(`/lnurl-withdrawal`, lnUrlWithdrawal); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { OpenNodeClient } from "./client"; 2 | import { OpenNodeEnv, v1, v2 } from "./types"; 3 | 4 | export { OpenNodeClient } from "./client"; 5 | export { OpenNodeError } from "./OpenNodeError"; 6 | export * from "./types"; 7 | 8 | let instance!: OpenNodeClient; 9 | 10 | export function setCredentials( 11 | key: string, 12 | environment: OpenNodeEnv = "live" 13 | ): void { 14 | if (instance !== undefined) { 15 | process.emitWarning( 16 | "opennode.setCredentials was called multiple times", 17 | "OpenNode-SetCred", 18 | "OPND001" 19 | ); 20 | return; 21 | } 22 | 23 | instance = new OpenNodeClient(key, environment); 24 | } 25 | 26 | export function createCharge( 27 | charge: v1.OpenNodeChargeRequest 28 | ): Promise { 29 | return instance.createCharge(charge); 30 | } 31 | 32 | export function chargeInfo(id: string): Promise { 33 | return instance.chargeInfo(id); 34 | } 35 | 36 | export function listCharges(): Promise { 37 | return instance.listCharges(); 38 | } 39 | 40 | export function initiateWithdrawal( 41 | withdrawal: v1.OpenNodeWithdrawalRequest 42 | ): Promise { 43 | return instance.initiateWithdrawal(withdrawal); 44 | } 45 | 46 | export function initiateExchange( 47 | exchange: v2.OpenNodeExchangeRequest 48 | ): Promise { 49 | return instance.initiateExchange(exchange); 50 | } 51 | 52 | export function withdrawalInfo(id: string): Promise { 53 | return instance.withdrawalInfo(id); 54 | } 55 | 56 | export function listWithdrawals(): Promise { 57 | return instance.listWithdrawals(); 58 | } 59 | 60 | export function listRates(): Promise { 61 | return instance.listRates(); 62 | } 63 | 64 | export function listCurrencies(): Promise { 65 | return instance.listCurrencies(); 66 | } 67 | 68 | /** @deprecated Use {@link accountBalance} */ 69 | export function getBalance(): Promise { 70 | return instance.userBalance(); 71 | } 72 | 73 | export function accountBalance(): Promise { 74 | return instance.accountBalance(); 75 | } 76 | 77 | export function initiateWithdrawalAsync( 78 | withdrawal: v2.OpenNodeWithdrawalOnchainRequest 79 | ): Promise { 80 | return instance.initiateWithdrawalAsync(withdrawal); 81 | } 82 | 83 | export function signatureIsValid(charge: v1.OpenNodeChargeWebhook): boolean { 84 | return instance.verifySignature(charge); 85 | } 86 | 87 | export function refundCharge( 88 | refund: v1.OpenNodeRefundRequest 89 | ): Promise { 90 | return instance.refundCharge(refund); 91 | } 92 | 93 | export function listRefunds(): Promise { 94 | return instance.listRefunds(); 95 | } 96 | 97 | export function refundInfo(id: string): Promise { 98 | return instance.refundInfo(id); 99 | } 100 | 101 | export function initiatePayout( 102 | payout: v2.OpenNodePayoutRequest 103 | ): Promise { 104 | return instance.initiatePayout(payout); 105 | } 106 | 107 | export function payoutInfo(id: string): Promise { 108 | return instance.payoutInfo(id); 109 | } 110 | 111 | export function createLnUrlWithdrawal( 112 | withdrawal: v2.OpenNodeLnURLWithdrawalRequest 113 | ): Promise { 114 | return instance.createLnUrlWithdrawal(withdrawal); 115 | } 116 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * as v1 from "./v1"; 2 | export * as v2 from "./v2"; 3 | 4 | export type OpenNodeEnv = "live" | "dev"; 5 | -------------------------------------------------------------------------------- /src/types/v1.ts: -------------------------------------------------------------------------------- 1 | export interface OpenNodeChargeRequest { 2 | /** 3 | * Amount intended to be collected. 4 | * The default currency is in 'satoshis', specify 'currency' param for fiat amounts 5 | */ 6 | amount: number; 7 | 8 | /** 9 | * Three-letter ISO currency code, in uppercase. 10 | * If specified will generate a charge for the equivalent amount of BTC at the current exchange rates 11 | */ 12 | currency?: string; 13 | 14 | /** Arbitrary description string that will be attached to the charge */ 15 | description?: string; 16 | 17 | /** Payers email for merchants internal use */ 18 | customer_email?: string; 19 | 20 | /** Payers email for sending payment receipt after successful payment */ 21 | notif_email?: string; 22 | 23 | /** Payers name for merchants internal use */ 24 | customer_name?: string; 25 | 26 | /** Order id for merchants internal use */ 27 | order_id?: string; 28 | 29 | /** URL to receive webhooks for payment status updates */ 30 | callback_url?: string; 31 | 32 | /** URL to redirect user after successful payment */ 33 | success_url?: string; 34 | 35 | /** Auto convert payment to fiat on successful payment (account fiat wallet must be enabled) */ 36 | auto_settle?: boolean; 37 | 38 | /** 39 | * time to live in minutes. 40 | * 41 | * Min: `10` \ 42 | * Max(Default): `1440` (24H) 43 | */ 44 | ttl?: number; 45 | 46 | metadata?: Record; 47 | 48 | /** 49 | * Basis points to auto convert checkout to `BTC` 50 | * 51 | * Max: `10000` (100%) 52 | */ 53 | split_to_btc_bps?: number; 54 | 55 | /** Whether OpenNode should send an email to {@link notif_email} */ 56 | notify_receiver?: boolean; 57 | 58 | /** Whether OpenNode should require KYC check of the payer */ 59 | kyc_required?: boolean; 60 | 61 | /** Whether description should only be shown as a sha256 hash */ 62 | desc_hash?: string; 63 | } 64 | 65 | export type OpenNodeChargeWebhook = { hashed_order: string } & OpenNodeCharge; 66 | 67 | export interface OpenNodeCharge { 68 | id: string; 69 | description: string; 70 | amount: number; 71 | missing_amt: number; 72 | status: string; 73 | fiat_value: number; 74 | source_fiat_value: number; 75 | currency: string; 76 | created_at: number; 77 | order_id: string; 78 | address: string; 79 | metadata?: OpenNodeChargeMetadata; 80 | expires_at?: string; 81 | auto_settle?: boolean; 82 | chain_invoice?: OpenNodeOnchainInvoice; 83 | transactions?: OpenNodeChargeTransaction[]; 84 | } 85 | 86 | export interface OpenNodeOnchainInvoice { 87 | address: string; 88 | settled_at: number; 89 | tx: string; 90 | } 91 | 92 | export interface OpenNodeChargeTransaction { 93 | address: string; 94 | created_at: number; 95 | settled_at: number; 96 | tx: string; 97 | status: string; 98 | amount: number; 99 | } 100 | 101 | export interface OpenNodeChargeMetadata { 102 | email: string; 103 | invoice_id: string; 104 | } 105 | 106 | export interface OpenNodeWithdrawalRequest { 107 | /** Determines type of the withdrawal. */ 108 | type: "chain" | "ln" | "wire"; 109 | 110 | /** 111 | * Amount intended to be withdrawn from the account. 112 | * 113 | * Required for type "chain" and "wire". 114 | * 115 | * Amount in satoshis when type "chain" and "ln". \ 116 | * Amount in user's fiat currency when type "wire". 117 | */ 118 | amount?: number; 119 | 120 | /** 121 | * Address that funds will be withdrawn to. 122 | * 123 | * Required for type "chain" and "ln" withdrawals. \ 124 | * Type "chain": On-chain address \ 125 | * Type "ln": Lightning Payment request. 126 | */ 127 | address?: string; 128 | 129 | /** URL to receive webhooks for withdrawal status updates */ 130 | callback_url?: string; 131 | } 132 | 133 | export interface OpenNodeWithdrawal { 134 | id: string; 135 | email: string; 136 | address: string; 137 | amount: string; 138 | fee: string; 139 | tx: string; 140 | status: string; 141 | created_at: number; 142 | processed_at: string; 143 | checkout_id: string; 144 | } 145 | 146 | /** 147 | * To avoid listing all possible currencies as nullable we set it as a record. 148 | * 149 | * E.g. if you account has USD as currency then it would be `{ USD: number }` 150 | */ 151 | type UserCurrencyRecord = { 152 | [fiatIso: string]: number; 153 | }; 154 | 155 | export type OpenNodeRates = Record< 156 | string, 157 | { BTC: number; currency: string } & UserCurrencyRecord 158 | >; 159 | 160 | export interface OpenNodeBalance { 161 | balance: { 162 | BTC: number; 163 | } & UserCurrencyRecord; 164 | } 165 | 166 | export interface OpenNodeRefundRequest { 167 | /** Underpaid charge ID */ 168 | checkout_id: string; 169 | 170 | /** Bitcoin address to send the funds */ 171 | address: string; 172 | 173 | /** Buyer email to get notified of the refund */ 174 | email?: string; 175 | } 176 | 177 | export interface OpenNodeRefund { 178 | id: string; 179 | email: string; 180 | address: string; 181 | amount: string; 182 | fee: string; 183 | tx: string; 184 | status: string; 185 | created_at: number; 186 | processed_at: string; 187 | checkout_id: string; 188 | } 189 | -------------------------------------------------------------------------------- /src/types/v2.ts: -------------------------------------------------------------------------------- 1 | export interface OpenNodePayoutTransferRequest { 2 | /** Use `id` for internal transfers. `managed` to transfers to managed accounts */ 3 | type: "id" | "managed"; 4 | /** Recipient's OpenNode ID (see Business profile page on dashboard) or managed account ID */ 5 | address: string; 6 | /** Amount (denominated by {@link currency}) */ 7 | amount: string; 8 | /** Currency of that transfer (if not set defaults to the same currenct as the payout source wallet) */ 9 | currency?: string; 10 | } 11 | 12 | export interface OpenNodePayoutRequest { 13 | /** Your source wallet (e.g: BTC or USD/EUR/GBP) */ 14 | wallet: string; 15 | 16 | transfers: OpenNodePayoutTransferRequest[]; 17 | 18 | /** URL to receive webhooks for payout status updates */ 19 | webhook_url?: string; 20 | 21 | external_funding?: boolean; 22 | } 23 | 24 | export interface OpenNodePayout { 25 | id: string; 26 | amount: number; 27 | status: string; 28 | currency: string; 29 | processed_at: number; 30 | created_at: number; 31 | fee: number; 32 | source_amount: number; 33 | source_fee: number; 34 | source_wallet: string; 35 | transfers: { id: string }[]; 36 | external_funding: boolean; 37 | } 38 | 39 | export type OpenNodeExchangeRequest = { 40 | to: "btc" | "fiat"; 41 | 42 | /** Amount intended to be exchanged in merchant's local currency. */ 43 | fiat_amount?: number; 44 | 45 | /** Amount intended to be exchanged in satoshis. */ 46 | btc_amount?: number; 47 | 48 | /** Defaults to `true` */ 49 | auto_confirm?: boolean; 50 | }; 51 | 52 | export interface OpenNodeExchange { 53 | success: boolean; 54 | id: string; 55 | btc_amount: number; 56 | fiat_amount: number; 57 | fiat_currency: string; 58 | expires_at: number; 59 | } 60 | 61 | export type OpenNodeWithdrawalRequest = 62 | | OpenNodeWithdrawalOnchainRequest 63 | | OpenNodeWithdrawalLnRequest 64 | | OpenNodeWithdrawalWireRequest; 65 | 66 | export interface OpenNodeWithdrawalOnchainRequest { 67 | /** 68 | * Type `chain` (on-chain) 69 | */ 70 | type: "chain"; 71 | 72 | /** 73 | * Amount intended to be withdrawn from the account (denominated by {@link currency}). 74 | */ 75 | amount: number; 76 | 77 | /** Defaults to `BTC` */ 78 | currency?: string; 79 | 80 | /** 81 | * On-chain address that funds will be withdrawn to. 82 | */ 83 | address: string; 84 | 85 | /** 86 | * URL to receive webhooks for withdrawal status updates 87 | */ 88 | callback_url?: string; 89 | 90 | /** Source of funds to perform withdrawal. If `fiat` will convert the necessary amount and withdrawal */ 91 | wallet?: "fiat" | "btc"; 92 | 93 | /** Defaults to `true` */ 94 | auto_confirm?: boolean; 95 | 96 | custom_id?: string; 97 | } 98 | 99 | export interface OpenNodeWithdrawalLnRequest { 100 | /** 101 | * Type `ln` (Lightning Network) 102 | */ 103 | type: "ln"; 104 | 105 | /** 106 | * Lightning Payment request. 107 | */ 108 | payreq: string; 109 | 110 | /** 111 | * Amount intended to be withdrawn from the account (denominated in `sats` - only needed when payreq doesn't have amount). 112 | */ 113 | amount?: number; 114 | 115 | /** 116 | * URL to receive webhooks for withdrawal status updates 117 | */ 118 | callback_url?: string; 119 | 120 | /** Source of funds to perform withdrawal. If `fiat` will convert the necessary amount and withdrawal */ 121 | wallet?: "fiat" | "btc"; 122 | 123 | /** Defaults to `true` */ 124 | auto_confirm?: boolean; 125 | 126 | custom_id?: string; 127 | } 128 | 129 | export interface OpenNodeWithdrawalWireRequest { 130 | /** 131 | * Type `wire` (bank transfer) 132 | */ 133 | type: "wire"; 134 | 135 | /** 136 | * Amount in user's fiat currency. 137 | */ 138 | amount: number; 139 | } 140 | 141 | export interface OpenNodeWithdrawal { 142 | id: string; 143 | reference: string; 144 | type: string; 145 | amount: number; 146 | fee: number; 147 | fiat_value: number; 148 | custom_id: string; 149 | callback_url: string; 150 | processed_at: number; 151 | conversion?: { 152 | from: string; 153 | from_amount: number; 154 | to: string; 155 | to_amount: number; 156 | }; 157 | expiry?: number; 158 | status: string; 159 | } 160 | 161 | export interface OpenNodeLnURLWithdrawalRequest { 162 | min_amt: number; 163 | max_amt: number; 164 | description: string; 165 | callback_url?: string; 166 | external_id?: string; 167 | expiry_date?: number; 168 | } 169 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const { assert, expect } = require('chai'); 2 | const opennode = require('../dist'); 3 | 4 | opennode.setCredentials('1f758b02-3405-45cf-8d71-f4462282a7ca', 'dev'); 5 | 6 | describe('charge', function () { 7 | describe('charge info', function () { 8 | it('should return a charge object', async () => { 9 | 10 | let charge, err 11 | 12 | try { 13 | charge = await opennode.chargeInfo('47bb5224-bf50-49d0-a317-4adfd345221a'); 14 | } catch (error) { 15 | err = error; 16 | } 17 | finally { 18 | expect(err).to.be.an('undefined'); 19 | assert.deepEqual(charge.id, '47bb5224-bf50-49d0-a317-4adfd345221a'); 20 | } 21 | 22 | }); 23 | }); 24 | 25 | describe('create charge', function () { 26 | it('should return a charge object', async () => { 27 | 28 | let charge, err 29 | 30 | try { 31 | charge = await opennode.createCharge({ 32 | amount: 10.5, 33 | currency: "USD", 34 | callback_url: "https://example.com/webhook/opennode", 35 | auto_settle: false 36 | }); 37 | } catch (error) { 38 | err = error; 39 | } 40 | finally { 41 | expect(err).to.be.an('undefined'); 42 | expect(charge).to.have.ownProperty('id'); 43 | } 44 | }); 45 | }); 46 | 47 | describe('create charge with invalid parameters', function () { 48 | it('should return a (400) Bad Request', async () => { 49 | 50 | let charge, err 51 | 52 | try { 53 | charge = await opennode.createCharge({ 54 | "description": "hello world" 55 | }); 56 | } catch (error) { 57 | err = error; 58 | } 59 | finally { 60 | assert.deepEqual(err.status, 400); 61 | } 62 | }); 63 | }); 64 | }); 65 | 66 | describe('utils', function () { 67 | describe('list currencies', function () { 68 | it('should return an object with supported currencies', async () => { 69 | 70 | let currencies, err; 71 | 72 | try { 73 | currencies = await opennode.listCurrencies(); 74 | } catch (error) { 75 | err = error; 76 | } 77 | finally { 78 | expect(err).to.be.an('undefined'); 79 | expect(currencies).to.an('array'); 80 | } 81 | }); 82 | }); 83 | 84 | describe('list rates', function () { 85 | it('should return an object with live rates', async () => { 86 | 87 | let rates, err; 88 | 89 | try { 90 | rates = await opennode.listRates(); 91 | } catch (error) { 92 | err = error; 93 | } 94 | finally { 95 | expect(err).to.be.an('undefined'); 96 | expect(rates).to.an('object'); 97 | } 98 | }); 99 | }); 100 | }); 101 | 102 | describe('withdrawals', function () { 103 | describe('get withdrawal info', function () { 104 | it('should return a withdrawal object', async () => { 105 | 106 | let rates, err; 107 | 108 | try { 109 | rates = await opennode.withdrawalInfo('73488531-d391-4de5-9468-0f72df85220c'); 110 | } catch (error) { 111 | err = error; 112 | } 113 | finally { 114 | expect(err).to.be.an('undefined'); 115 | expect(rates).to.an('object'); 116 | } 117 | }); 118 | }); 119 | 120 | describe('create a withdrawal with wrong permissions', function () { 121 | it('should return a (403) Unauthorized', async () => { 122 | 123 | let withdrawal, err; 124 | 125 | try { 126 | withdrawal = await opennode.initiateWithdrawalAsync({ 127 | amount: 100000, 128 | type: "chain" 129 | }); 130 | } catch (error) { 131 | err = error; 132 | } 133 | finally { 134 | assert.equal(err.status, 403); 135 | } 136 | }); 137 | }); 138 | }); 139 | 140 | describe('webhook', function () { 141 | describe('verify webhook signature', function () { 142 | it('signatures should match', async () => { 143 | 144 | let result, err; 145 | const charge = { 146 | id: '22b13c2f-3297-422d-9e7c-4f9a35651d38', 147 | hashed_order: 'ec3978d14a2d547a174fd4980f562f1f4a12953f069d60844f99088c7cd77f1b' 148 | }; 149 | 150 | try { 151 | result = await opennode.signatureIsValid(charge); 152 | } catch (error) { 153 | err = error; 154 | } 155 | finally { 156 | expect(result).to.be.true; 157 | expect(err).to.be.an('undefined'); 158 | } 159 | }); 160 | }); 161 | }); 162 | 163 | describe('account', function () { 164 | describe('get user balance', function () { 165 | it('should return a balance object', async () => { 166 | 167 | let balance, err; 168 | 169 | try { 170 | balance = await opennode.getBalance(); 171 | } catch (error) { 172 | err = error; 173 | } 174 | finally { 175 | expect(err).to.be.an('undefined'); 176 | expect(balance).to.an('object'); 177 | } 178 | }); 179 | }); 180 | }); 181 | 182 | describe('refunds', function () { 183 | describe('refund charge', function () { 184 | it('should return a refund object', async () => { 185 | 186 | let refund, err; 187 | 188 | try { 189 | refund = await opennode.refundCharge({ 190 | checkout_id: '5af57c22-9855-41ae-a161-65ba625d7613', 191 | address: 'tb1quvqmj4kzu4v9308wg300ysv5yl7ta80a8yrdn7' 192 | }); 193 | } catch (error) { 194 | err = error; 195 | } 196 | finally { 197 | assert.deepEqual(err.status, 400); 198 | expect(refund).to.be.an('undefined'); 199 | } 200 | }); 201 | }); 202 | 203 | describe('refund info', function () { 204 | it('should return a refund object', async () => { 205 | 206 | let refund, err; 207 | 208 | try { 209 | refund = await opennode.refundInfo('6b57c77e-eab4-4b39-8146-121f1d0fe8ba'); 210 | } catch (error) { 211 | err = error; 212 | } 213 | finally { 214 | assert.deepEqual(err.status, 400); 215 | expect(refund).to.be.an('undefined'); 216 | } 217 | }); 218 | }); 219 | 220 | describe('list refunds', function () { 221 | it('should return a list of refunds', async () => { 222 | 223 | let refunds, err; 224 | 225 | try { 226 | refunds = await opennode.listRefunds(); 227 | } catch (error) { 228 | err = error; 229 | } 230 | finally { 231 | expect(err).to.be.an('undefined'); 232 | expect(refunds).to.an('array'); 233 | } 234 | }); 235 | }); 236 | }); 237 | 238 | describe('client', function () { 239 | it('should allow multiple clients with different credentials', async () => { 240 | 241 | let charge1, charge2, err 242 | const client1 = new opennode.OpenNodeClient('1f758b02-3405-45cf-8d71-f4462282a7ca', 'dev'); 243 | const charge1Id = '47bb5224-bf50-49d0-a317-4adfd345221a'; 244 | const client2 = new opennode.OpenNodeClient('195d82c3-96de-43a3-9de2-13fc7fca7c7c', 'dev'); 245 | const charge2Id = 'd09fc8f0-8d51-4292-adeb-f8dd951fb7e6'; 246 | 247 | try { 248 | charge1 = await client1.chargeInfo(charge1Id); 249 | charge2 = await client2.chargeInfo(charge2Id); 250 | } catch (error) { 251 | err = error; 252 | } 253 | finally { 254 | expect(err).to.be.an('undefined'); 255 | assert.deepEqual(charge1.id, charge1Id); 256 | assert.deepEqual(charge2.id, charge2Id); 257 | } 258 | }); 259 | }); 260 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "moduleResolution": "node", 5 | "target": "ES2020", 6 | "strictNullChecks": true, 7 | "strictFunctionTypes": true, 8 | "outDir": "dist", 9 | "rootDir": "src", 10 | "sourceMap": true, 11 | "declaration": true, 12 | "declarationMap": true, 13 | "pretty": false, 14 | "removeComments": true, 15 | "noImplicitReturns": true, 16 | "noImplicitAny": true, 17 | "allowJs": false, 18 | "alwaysStrict": true, 19 | "resolveJsonModule": true, 20 | "esModuleInterop": true 21 | }, 22 | "files": [ 23 | "src/index.ts", 24 | ], 25 | "include": [ 26 | "src/" 27 | ], 28 | "exclude": [ 29 | "node_modules", 30 | "**/node_modules/*" 31 | ] 32 | } 33 | --------------------------------------------------------------------------------