Live status of transaction

This commit is contained in:
2020-12-27 23:27:35 +01:00
parent 8168674516
commit 7f8ae69e2e
12 changed files with 791 additions and 127 deletions

11
Socket Events.md Normal file
View File

@@ -0,0 +1,11 @@
# Socket events
## Invoice status
### Requests
* `subscribe` - Subscribe to a invoices progress. Returns `true` if successful.
* `selector` - Your selector
### Events
* `status` - Status changed (see PaymentStatus enum)
* `confirmationUpdate` - When there is a new confirmation on the transaction
* `count` - Count of confirmations

View File

@@ -14,6 +14,10 @@ export const config: IConfig = {
http: { http: {
port: 2009, port: 2009,
host: "0.0.0.0" host: "0.0.0.0"
},
transcations: {
// If a payment has been made and its value is this amount less, it would be still accepted.
acceptMargin: 0.00000001
} }
} }
/** /**
@@ -33,5 +37,8 @@ export interface IConfig {
http: { http: {
port: number, port: number,
host: string host: string
},
transcations: {
acceptMargin: number
} }
} }

533
package-lock.json generated
View File

@@ -1,5 +1,5 @@
{ {
"name": "uplata", "name": "librepay",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
@@ -19,6 +19,19 @@
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
"integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==" "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ=="
}, },
"@sindresorhus/is": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz",
"integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ=="
},
"@szmarczak/http-timer": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
"integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
"requires": {
"defer-to-connect": "^2.0.0"
}
},
"@types/argon2": { "@types/argon2": {
"version": "0.15.0", "version": "0.15.0",
"resolved": "https://registry.npmjs.org/@types/argon2/-/argon2-0.15.0.tgz", "resolved": "https://registry.npmjs.org/@types/argon2/-/argon2-0.15.0.tgz",
@@ -46,10 +59,16 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/component-emitter": { "@types/cacheable-request": {
"version": "1.2.10", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
"requires": {
"@types/http-cache-semantics": "*",
"@types/keyv": "*",
"@types/node": "*",
"@types/responselike": "*"
}
}, },
"@types/connect": { "@types/connect": {
"version": "3.4.34", "version": "3.4.34",
@@ -59,15 +78,11 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg=="
},
"@types/cors": { "@types/cors": {
"version": "2.8.9", "version": "2.8.9",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.9.tgz", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.9.tgz",
"integrity": "sha512-zurD1ibz21BRlAOIKP8yhrxlqKx6L9VCwkB5kMiP6nZAhoF5MvC7qS1qPA7nRcr1GJolfkQC7/EAL4hdYejLtg==" "integrity": "sha512-zurD1ibz21BRlAOIKP8yhrxlqKx6L9VCwkB5kMiP6nZAhoF5MvC7qS1qPA7nRcr1GJolfkQC7/EAL4hdYejLtg==",
"dev": true
}, },
"@types/dotenv": { "@types/dotenv": {
"version": "8.2.0", "version": "8.2.0",
@@ -109,6 +124,11 @@
"@types/range-parser": "*" "@types/range-parser": "*"
} }
}, },
"@types/http-cache-semantics": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
"integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A=="
},
"@types/jsonwebtoken": { "@types/jsonwebtoken": {
"version": "8.5.0", "version": "8.5.0",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz",
@@ -118,6 +138,14 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/keyv": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
"integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
"requires": {
"@types/node": "*"
}
},
"@types/lodash": { "@types/lodash": {
"version": "4.14.165", "version": "4.14.165",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz",
@@ -172,6 +200,14 @@
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
}, },
"@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
"requires": {
"@types/node": "*"
}
},
"@types/serve-static": { "@types/serve-static": {
"version": "1.13.8", "version": "1.13.8",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz",
@@ -252,6 +288,11 @@
"negotiator": "0.6.2" "negotiator": "0.6.2"
} }
}, },
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
},
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
@@ -311,11 +352,26 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
}, },
"arraybuffer.slice": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
},
"async": { "async": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
}, },
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -331,6 +387,14 @@
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
}, },
"better-assert": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
"requires": {
"callsite": "1.0.0"
}
},
"bignumber.js": { "bignumber.js": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
@@ -369,6 +433,11 @@
} }
} }
}, },
"blob": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
},
"bluebird": { "bluebird": {
"version": "3.5.1", "version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
@@ -420,11 +489,43 @@
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
}, },
"cacheable-lookup": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="
},
"cacheable-request": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz",
"integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==",
"requires": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
"http-cache-semantics": "^4.0.0",
"keyv": "^4.0.0",
"lowercase-keys": "^2.0.0",
"normalize-url": "^4.1.0",
"responselike": "^2.0.0"
}
},
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
},
"chownr": { "chownr": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
}, },
"clone-response": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
"integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
"requires": {
"mimic-response": "^1.0.0"
}
},
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@@ -487,10 +588,20 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
}, },
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": { "component-emitter": {
"version": "1.3.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
"component-inherit": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
@@ -552,11 +663,31 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"requires": {
"mimic-response": "^3.1.0"
},
"dependencies": {
"mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
}
}
},
"deep-extend": { "deep-extend": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
}, },
"defer-to-connect": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz",
"integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg=="
},
"delegates": { "delegates": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -615,24 +746,31 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
}, },
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"engine.io": { "engine.io": {
"version": "4.0.5", "version": "3.4.2",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.5.tgz", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz",
"integrity": "sha512-Ri+whTNr2PKklxQkfbGjwEo+kCBUM4Qxk4wtLqLrhH+b1up2NFL9g9pjYWiCV/oazwB0rArnvF/ZmZN2ab5Hpg==", "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==",
"requires": { "requires": {
"accepts": "~1.3.4", "accepts": "~1.3.4",
"base64id": "2.0.0", "base64id": "2.0.0",
"cookie": "~0.4.1", "cookie": "0.3.1",
"cors": "~2.8.5",
"debug": "~4.1.0", "debug": "~4.1.0",
"engine.io-parser": "~4.0.0", "engine.io-parser": "~2.2.0",
"ws": "^7.1.2" "ws": "^7.1.2"
}, },
"dependencies": { "dependencies": {
"cookie": { "cookie": {
"version": "0.4.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
}, },
"debug": { "debug": {
"version": "4.1.1", "version": "4.1.1",
@@ -649,12 +787,67 @@
} }
} }
}, },
"engine.io-parser": { "engine.io-client": {
"version": "4.0.2", "version": "3.4.4",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz",
"integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==",
"requires": { "requires": {
"base64-arraybuffer": "0.1.4" "component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
"debug": "~3.1.0",
"engine.io-parser": "~2.2.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"ws": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
"integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
"engine.io-parser": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
"integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.4",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
} }
}, },
"es6-promise": { "es6-promise": {
@@ -822,6 +1015,14 @@
} }
} }
}, },
"get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"requires": {
"pump": "^3.0.0"
}
},
"glob": { "glob": {
"version": "7.1.6", "version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -835,11 +1036,54 @@
"path-is-absolute": "^1.0.0" "path-is-absolute": "^1.0.0"
} }
}, },
"got": {
"version": "11.8.1",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.1.tgz",
"integrity": "sha512-9aYdZL+6nHmvJwHALLwKSUZ0hMwGaJGYv3hoPLPgnT8BoBXm1SjnZeky+91tfwJaDzun2s4RsBRy48IEYv2q2Q==",
"requires": {
"@sindresorhus/is": "^4.0.0",
"@szmarczak/http-timer": "^4.0.5",
"@types/cacheable-request": "^6.0.1",
"@types/responselike": "^1.0.0",
"cacheable-lookup": "^5.0.3",
"cacheable-request": "^7.0.1",
"decompress-response": "^6.0.0",
"http2-wrapper": "^1.0.0-beta.5.2",
"lowercase-keys": "^2.0.0",
"p-cancelable": "^2.0.0",
"responselike": "^2.0.0"
}
},
"has-binary2": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
"requires": {
"isarray": "2.0.1"
},
"dependencies": {
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
}
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"has-unicode": { "has-unicode": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
}, },
"http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
},
"http-errors": { "http-errors": {
"version": "1.7.2", "version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
@@ -852,6 +1096,15 @@
"toidentifier": "1.0.0" "toidentifier": "1.0.0"
} }
}, },
"http2-wrapper": {
"version": "1.0.0-beta.5.2",
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz",
"integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==",
"requires": {
"quick-lru": "^5.1.1",
"resolve-alpn": "^1.0.0"
}
},
"iconv-lite": { "iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -868,6 +1121,11 @@
"minimatch": "^3.0.4" "minimatch": "^3.0.4"
} }
}, },
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"inflight": { "inflight": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -925,6 +1183,11 @@
"uuid": "^3.4.0" "uuid": "^3.4.0"
} }
}, },
"json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
"json-stringify-safe": { "json-stringify-safe": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -983,6 +1246,14 @@
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz",
"integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ=="
}, },
"keyv": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
"integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==",
"requires": {
"json-buffer": "3.0.1"
}
},
"kuler": { "kuler": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
@@ -1047,6 +1318,11 @@
} }
} }
}, },
"lowercase-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
},
"make-error": { "make-error": {
"version": "1.3.6", "version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@@ -1091,6 +1367,11 @@
"mime-db": "1.44.0" "mime-db": "1.44.0"
} }
}, },
"mimic-response": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
},
"minimatch": { "minimatch": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -1313,6 +1594,11 @@
"osenv": "^0.1.4" "osenv": "^0.1.4"
} }
}, },
"normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ=="
},
"npm-bundled": { "npm-bundled": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
@@ -1357,6 +1643,11 @@
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
}, },
"object-component": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
},
"on-finished": { "on-finished": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@@ -1405,6 +1696,27 @@
"os-tmpdir": "^1.0.0" "os-tmpdir": "^1.0.0"
} }
}, },
"p-cancelable": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz",
"integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg=="
},
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
"integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseuri": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseurl": { "parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -1434,11 +1746,25 @@
"ipaddr.js": "1.9.1" "ipaddr.js": "1.9.1"
} }
}, },
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"qs": { "qs": {
"version": "6.7.0", "version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
}, },
"quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
},
"range-parser": { "range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -1490,11 +1816,24 @@
"semver": "^5.1.0" "semver": "^5.1.0"
} }
}, },
"resolve-alpn": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz",
"integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA=="
},
"resolve-from": { "resolve-from": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
"integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
}, },
"responselike": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
"requires": {
"lowercase-keys": "^2.0.0"
}
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -1604,26 +1943,18 @@
"integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
}, },
"socket.io": { "socket.io": {
"version": "3.0.4", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.4.tgz", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
"integrity": "sha512-Vj1jUoO75WGc9txWd311ZJJqS9Dr8QtNJJ7gk2r7dcM/yGe9sit7qOijQl3GAwhpBOz/W8CwkD7R6yob07nLbA==", "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
"requires": { "requires": {
"@types/cookie": "^0.4.0",
"@types/cors": "^2.8.8",
"@types/node": "^14.14.7",
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.1.0", "debug": "~4.1.0",
"engine.io": "~4.0.0", "engine.io": "~3.4.0",
"socket.io-adapter": "~2.0.3", "has-binary2": "~1.0.2",
"socket.io-parser": "~4.0.1" "socket.io-adapter": "~1.1.0",
"socket.io-client": "2.3.0",
"socket.io-parser": "~3.4.0"
}, },
"dependencies": { "dependencies": {
"@types/node": {
"version": "14.14.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.16.tgz",
"integrity": "sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw=="
},
"debug": { "debug": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@@ -1640,18 +1971,94 @@
} }
}, },
"socket.io-adapter": { "socket.io-adapter": {
"version": "2.0.3", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.0.3.tgz", "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
"integrity": "sha512-2wo4EXgxOGSFueqvHAdnmi5JLZzWqMArjuP4nqC26AtLh5PoCPsaRbRdah2xhcwTAMooZfjYiNVNkkmmSMaxOQ==" "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
"socket.io-client": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
"integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"socket.io-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
"integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==",
"requires": {
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
}
}
}, },
"socket.io-parser": { "socket.io-parser": {
"version": "4.0.2", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.2.tgz", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
"integrity": "sha512-Bs3IYHDivwf+bAAuW/8xwJgIiBNtlvnjYRc4PbXgniLmcP1BrakBoq/QhO24rgtgW7VZ7uAaswRGxutUnlAK7g==", "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
"requires": { "requires": {
"@types/component-emitter": "^1.2.10", "component-emitter": "1.2.1",
"component-emitter": "~1.3.0", "debug": "~4.1.0",
"debug": "~4.1.0" "isarray": "2.0.1"
}, },
"dependencies": { "dependencies": {
"debug": { "debug": {
@@ -1662,6 +2069,11 @@
"ms": "^2.1.1" "ms": "^2.1.1"
} }
}, },
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"ms": { "ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -1761,6 +2173,11 @@
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
}, },
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
},
"toidentifier": { "toidentifier": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
@@ -1919,11 +2336,21 @@
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz",
"integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==" "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ=="
}, },
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
},
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
}, },
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"yn": { "yn": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",

View File

@@ -1,5 +1,5 @@
{ {
"name": "uplata", "name": "librepay",
"version": "1.0.0", "version": "1.0.0",
"description": "Next-gen payment processor for cryptocurrencies.", "description": "Next-gen payment processor for cryptocurrencies.",
"main": "index.js", "main": "index.js",
@@ -26,11 +26,12 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",
"got": "^11.8.1",
"jayson": "^3.4.4", "jayson": "^3.4.4",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"mongoose": "^5.11.8", "mongoose": "^5.11.8",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"socket.io": "^3.0.4", "socket.io": "^2.3.0",
"ts-node": "^9.1.1", "ts-node": "^9.1.1",
"typescript": "^4.1.3", "typescript": "^4.1.3",
"winston": "^3.3.3", "winston": "^3.3.3",

View File

@@ -5,6 +5,8 @@ import * as express from 'express';
import * as rpc from 'jayson'; import * as rpc from 'jayson';
import * as mongoose from 'mongoose'; import * as mongoose from 'mongoose';
import * as winston from 'winston'; import * as winston from 'winston';
import * as socketio from 'socket.io';
import { Server } from 'http';
import { config } from '../config'; import { config } from '../config';
import { hashPassword, randomPepper, randomString } from './helper/crypto'; import { hashPassword, randomPepper, randomString } from './helper/crypto';
@@ -12,6 +14,7 @@ import { InvoiceScheduler } from './helper/invoiceScheduler';
import { User } from './models/user/user.model'; import { User } from './models/user/user.model';
import { invoiceRouter } from './routes/invoice'; import { invoiceRouter } from './routes/invoice';
import { userRouter } from './routes/user'; import { userRouter } from './routes/user';
import { SocketManager } from './helper/socketio';
// Load .env // Load .env
dconfig({ debug: true, encoding: 'UTF-8' }); dconfig({ debug: true, encoding: 'UTF-8' });
@@ -23,6 +26,7 @@ export const INVOICE_SECRET = process.env.INVOICE_SECRET || "";
export let rpcClient: rpc.HttpClient | undefined = undefined; export let rpcClient: rpc.HttpClient | undefined = undefined;
export let invoiceScheduler: InvoiceScheduler | undefined = undefined; export let invoiceScheduler: InvoiceScheduler | undefined = undefined;
export let socketManager: SocketManager | undefined = undefined;
export let logger: winston.Logger; export let logger: winston.Logger;
@@ -105,15 +109,22 @@ async function run() {
invoiceScheduler = new InvoiceScheduler(); invoiceScheduler = new InvoiceScheduler();
const app = express(); const app = express();
const http = new Server(app);
// Socket.io
const io = new socketio(http);
socketManager = new SocketManager(io);
app.use(express.json()); app.use(express.json());
app.use(cors()); app.use(cors());
app.use(bodyParser.json({ limit: '2kb' })); app.use(bodyParser.json({ limit: '2kb' }));
app.get('/', (req, res) => res.status(200).send('OK'));
app.use('/invoice', invoiceRouter); app.use('/invoice', invoiceRouter);
app.use('/user', userRouter); app.use('/user', userRouter);
app.listen(config.http.port, config.http.host, () => { app.get('/', (req, res) => res.status(200).send('OK'));
http.listen(config.http.port, config.http.host, () => {
logger.info(`HTTP server started on port ${config.http.host}:${config.http.port}`); logger.info(`HTTP server started on port ${config.http.host}:${config.http.port}`);
}); });

View File

@@ -1,9 +1,12 @@
import { Request, Response } from "express"; import { Request, Response } from 'express';
import { invoiceScheduler, INVOICE_SECRET } from "../app"; import got from 'got';
import { CryptoUnits, FiatUnits } from "../helper/types";
import { ICart, IInvoice } from "../models/invoice/invoice.interface"; import { invoiceScheduler, INVOICE_SECRET, rpcClient } from '../app';
import { Invoice } from "../models/invoice/invoice.model"; import { randomString } from '../helper/crypto';
import { rpcClient } from '../app'; import { CryptoUnits, FiatUnits, findCryptoBySymbol, PaymentStatus } from '../helper/types';
import { ICart, IInvoice, IPaymentMethod } from '../models/invoice/invoice.interface';
import { Invoice } from '../models/invoice/invoice.model';
import { calculateCart } from '../models/invoice/invoice.schema';
// POST /invoice/?sercet=XYZ // POST /invoice/?sercet=XYZ
export async function createInvoice(req: Request, res: Response) { export async function createInvoice(req: Request, res: Response) {
@@ -20,14 +23,14 @@ export async function createInvoice(req: Request, res: Response) {
} }
} }
const paymentMethods: CryptoUnits[] = req.body.methods; const paymentMethodsRaw: string[] = req.body.methods;
const successUrl: string = req.body.successUrl; const successUrl: string = req.body.successUrl;
const cancelUrl: string = req.body.cancelUrl; const cancelUrl: string = req.body.cancelUrl;
const cart: ICart[] = req.body.cart; const cart: ICart[] = req.body.cart;
const currency: FiatUnits = req.body.currency; let currency: FiatUnits = req.body.currency;
const totalPrice: number = req.body.totalPrice; let totalPrice: number = req.body.totalPrice;
if (paymentMethods === undefined) { if (paymentMethodsRaw === undefined) {
res.status(400).send({ message: '"paymentMethods" are not provided!' }); res.status(400).send({ message: '"paymentMethods" are not provided!' });
return; return;
} }
@@ -45,19 +48,52 @@ export async function createInvoice(req: Request, res: Response) {
if (currency === undefined) { if (currency === undefined) {
res.status(400).send({ message: '"currency" is not provided!' }); res.status(400).send({ message: '"currency" is not provided!' });
return; return;
} else {
if (Object.keys(FiatUnits).indexOf(currency.toUpperCase()) === -1) {
res.status(400).send({ message: '"currency" can only be "eur" and "usd"' });
return;
} else {
currency = FiatUnits[currency.toUpperCase()];
}
} }
/*if (cart === undefined && totalPrice === undefined) { if (cart === undefined && totalPrice === undefined) {
res.status(400).send({ message: 'Either "cart" or "totalPrice" has to be defined.' }); res.status(400).send({ message: 'Either "cart" or "totalPrice" has to be defined.' });
return; return;
}*/ }
rpcClient.request('getnewaddress', ['', 'bech32'], async (err, response) => { rpcClient.request('getnewaddress', ['', 'bech32'], async (err, response) => {
if (err) throw err; if (err) throw err;
//console.log(response.result);
// Get price
// Convert coin symbol to full text in order to query Coin Gecko. eg.: ['btc', 'xmr'] => ['bitcoin', 'monero']
let cgFormat = [];
paymentMethodsRaw.forEach(coin => {
const crypto = findCryptoBySymbol(coin);
if (crypto !== undefined) {
cgFormat.push(crypto.toLowerCase());
}
});
const request = await got.get(`https://api.coingecko.com/api/v3/simple/price?ids=${cgFormat.join(',')}&vs_currencies=${currency.toLowerCase()}`, {
responseType: 'json'
});
// Calulate total price, if cart is provided
if (cart !== undefined && totalPrice === undefined) {
totalPrice = calculateCart(cart);
}
let paymentMethods: IPaymentMethod[] = [];
Object.keys(request.body).forEach(coin => {
paymentMethods.push({ method: CryptoUnits[coin.toUpperCase()], amount: totalPrice / Number(request.body[coin][currency.toLowerCase()]) });
});
Invoice.create({ Invoice.create({
paymentMethods, selector: randomString(128),
paymentMethods: paymentMethods,
successUrl, successUrl,
cancelUrl, cancelUrl,
cart, cart,
@@ -72,26 +108,37 @@ export async function createInvoice(req: Request, res: Response) {
} }
invoiceScheduler.addInvoice(invoice); invoiceScheduler.addInvoice(invoice);
res.status(200).send({ id: invoice.id }); res.status(200).send({ id: invoice.selector });
}); });
}); });
} }
// GET /invoice/ // GET /invoice/
// GET /invoice/:id // GET /invoice/:selector
export async function getInvoice(req: Request, res: Response) { export async function getInvoice(req: Request, res: Response) {
const invoiceId = req.params.id; const selector = req.params.selector;
// If an id is provided // If an id is provided
if (invoiceId !== undefined) { if (selector !== undefined) {
const invoice: any = await Invoice.findById(invoiceId); const invoice: IInvoice = await Invoice.findOne({ selector: selector });
if (invoice === null) { if (invoice === null) {
res.status(404).send(); res.status(404).send();
return; return;
} }
res.status(200).send(invoice); if(invoice.status === PaymentStatus.UNCONFIRMED || invoice.status === PaymentStatus.DONE) {
rpcClient.request('gettransaction', [invoice.transcationHashes[0]], (err, message) => {
let invoiceClone: any = invoice;
console.log(message.result.confirmations);
invoiceClone['confirmation'] = message.result.confirmations;
res.status(200).send(invoiceClone);
});
} else {
res.status(200).send(invoice);
}
return; return;
} }
@@ -117,4 +164,22 @@ export async function getInvoice(req: Request, res: Response) {
.sort({ createdAt: sort }); .sort({ createdAt: sort });
res.status(200).send(invoices); res.status(200).send(invoices);
}
// DELETE /invoice/:selector
export async function cancelPaymnet(req: Request, res: Response) {
const selector = req.params.selector;
// If an id is provided
if (selector !== undefined) {
const invoice = await Invoice.findOne({ selector: selector });
if (invoice === null) {
res.status(404).send();
return;
}
invoice.status = PaymentStatus.CANCELLED;
await invoice.save();
return;
}
} }

View File

@@ -1,18 +1,21 @@
import { IInvoice } from "../models/invoice/invoice.interface"; import { IInvoice } from "../models/invoice/invoice.interface";
import { Subscriber } from 'zeromq'; import { Subscriber } from 'zeromq';
import { logger, rpcClient } from "../app"; import { logger, rpcClient, socketManager } from "../app";
import { invoiceRouter } from "../routes/invoice"; import { invoiceRouter } from "../routes/invoice";
import { Invoice } from "../models/invoice/invoice.model"; import { Invoice } from "../models/invoice/invoice.model";
import { PaymentStatus } from "./types"; import { CryptoUnits, PaymentStatus } from "./types";
import { config } from "../../config";
export class InvoiceScheduler { export class InvoiceScheduler {
private pendingInvoices: IInvoice[]; private pendingInvoices: IInvoice[];
private unconfirmedTranscations: IInvoice[]; private unconfirmedTranscations: IInvoice[];
private knownConfirmations: Map<string, number>; // Invoice id / confirmation cound
private sock: Subscriber; private sock: Subscriber;
constructor() { constructor() {
this.unconfirmedTranscations = []; this.unconfirmedTranscations = [];
this.pendingInvoices = []; this.pendingInvoices = [];
this.knownConfirmations = new Map<string, number>();
// Get all pending transcations // Get all pending transcations
Invoice.find({ status: PaymentStatus.PENDING }).then(invoices => { Invoice.find({ status: PaymentStatus.PENDING }).then(invoices => {
@@ -44,7 +47,7 @@ export class InvoiceScheduler {
logger.info('Now listing for incoming transaction to any invoices ...'); logger.info('Now listing for incoming transaction to any invoices ...');
for await (const [topic, msg] of this.sock) { for await (const [topic, msg] of this.sock) {
const rawtx = msg.toString('hex'); const rawtx = msg.toString('hex');
logger.debug(`New tx: ${rawtx}`); //logger.debug(`New tx: ${rawtx}`);
rpcClient.request('decoderawtransaction', [rawtx], (err, decoded) => { rpcClient.request('decoderawtransaction', [rawtx], (err, decoded) => {
if (err) { if (err) {
logger.error(`Error while decoding raw tx: ${err.message}`); logger.error(`Error while decoding raw tx: ${err.message}`);
@@ -54,19 +57,29 @@ export class InvoiceScheduler {
decoded.result.vout.forEach(output => { decoded.result.vout.forEach(output => {
// Loop over each output and check if the address of one matches the one of an invoice. // Loop over each output and check if the address of one matches the one of an invoice.
this.pendingInvoices.forEach(invoice => { this.pendingInvoices.forEach(invoice => {
// We found our transaction
if (output.scriptPubKey.addresses === undefined) return; // Sometimes (weird) transaction don't have any addresses if (output.scriptPubKey.addresses === undefined) return; // Sometimes (weird) transaction don't have any addresses
// We found our transaction (https://developer.bitcoin.org/reference/rpc/decoderawtransaction.html)
if (output.scriptPubKey.addresses.indexOf(invoice.receiveAddress) !== -1) { if (output.scriptPubKey.addresses.indexOf(invoice.receiveAddress) !== -1) {
invoice.paid += output.value;
logger.info(`Transcation for invoice ${invoice.id} received! (${decoded.result.hash})`); logger.info(`Transcation for invoice ${invoice.id} received! (${decoded.result.hash})`);
// Change state in database // Change state in database
invoice.status = PaymentStatus.UNCONFIRMED; const price = invoice.paymentMethods.find((item) => { return item.method === CryptoUnits.BITCOIN }).amount;
invoice.transcationHash = decoded.result.txid; if (invoice.paid < price - config.transcations.acceptMargin) {
invoice.save(); const left = price - output.value;
invoice.status = PaymentStatus.PARTIALLY;
// Push to array & remove from pending invoice.save();
this.unconfirmedTranscations.push(invoice); logger.info(`Transcation for invoice ${invoice.id} received but there are still ${left} BTC missing (${decoded.result.hash})`);
this.pendingInvoices.splice(this.pendingInvoices.indexOf(invoice), 1); } else {
invoice.status = PaymentStatus.UNCONFIRMED;
invoice.transcationHashes.push(decoded.result.txid);
invoice.save();
// Push to array & remove from pending
this.unconfirmedTranscations.push(invoice);
this.pendingInvoices.splice(this.pendingInvoices.indexOf(invoice), 1);
}
} }
}) })
}); });
@@ -81,22 +94,40 @@ export class InvoiceScheduler {
private watchConfirmations() { private watchConfirmations() {
setInterval(() => { setInterval(() => {
this.unconfirmedTranscations.forEach(invoice => { this.unconfirmedTranscations.forEach(invoice => {
rpcClient.request('gettransaction', [invoice.transcationHash], (err, message) => { if (invoice.transcationHashes.length === 0) return;
if (err) { let trustworthy = true; // Will be true if all transactions are above threshold.
logger.error(`Error while fetching confirmation state of ${invoice.transcationHash}: ${err.message}`);
return;
}
if (Number(message.result.confirmations) > 2) { for (let i = 0; i < invoice.transcationHashes.length; i++) {
logger.info(`Transaction (${invoice.transcationHash}) has reached more then 2 confirmations and can now be trusted!`); const transcation = invoice.transcationHashes[i];
invoice.status = PaymentStatus.DONE;
invoice.save(); // This will trigger a post save hook that will notify the user. rpcClient.request('gettransaction', [transcation], (err, message) => {
if (err) {
logger.error(`Error while fetching confirmation state of ${transcation}: ${err.message}`);
trustworthy = false;
this.unconfirmedTranscations.splice(this.unconfirmedTranscations.indexOf(invoice), 1); return;
} else { }
logger.debug(`Transcation (${invoice.transcationHash}) has not reached his threshold yet.`);
} if (this.knownConfirmations.get(invoice.id) != message.result.confirmations) {
}); this.knownConfirmations.set(invoice.id, message.result.confirmations);
socketManager.getSocketByInvoice(invoice).emit('confirmationUpdate', { count: Number(message.result.confirmations) });
}
if (Number(message.result.confirmations) > 0) {
logger.info(`Transaction (${transcation}) has reached more then 2 confirmations and can now be trusted!`);
this.unconfirmedTranscations.splice(this.unconfirmedTranscations.indexOf(invoice), 1);
} else {
trustworthy = false;
logger.debug(`Transcation (${transcation}) has not reached his threshold yet.`);
}
});
}
if (trustworthy) {
invoice.status = PaymentStatus.DONE;
invoice.save(); // This will trigger a post save hook that will notify the user.
}
}); });
}, 2_000); }, 2_000);
} }

60
src/helper/socketio.ts Normal file
View File

@@ -0,0 +1,60 @@
import { Server, Socket } from "socket.io";
import { logger } from "../app";
import { IInvoice } from "../models/invoice/invoice.interface";
import { Invoice } from "../models/invoice/invoice.model";
import { PaymentStatus } from "./types";
export class SocketManager {
io: Server;
private socketInvoice: Map<string, string>; // Socket ID / _id
private idSocket: Map<string, Socket>; // Socket ID / Socket
private invoiceSocket: Map<string, Socket>; // _id / Socket
constructor(io: Server) {
this.io = io;
this.socketInvoice = new Map<string, string>();
this.idSocket = new Map<string, Socket>();
this.invoiceSocket = new Map<string, Socket>();
this.listen();
}
listen() {
console.log("Listen");
this.io.on('connection', (socket: Socket) => {
this.idSocket.set(socket.id, socket);
// The frontend sends his selector, then pick _id and put it in `socketInvoice` map.
// Return `true` if successful and `false` if not.
socket.on('subscribe', async data => {
if (data.selector !== undefined) {
const invoice = await Invoice.findOne({ selector: data.selector });
if (invoice === null) {
socket.emit('subscribe', false);
return;
}
logger.info(`Socket ${socket.id} has subscribed to invoice ${invoice.id} (${PaymentStatus[invoice.status]})`);
this.socketInvoice.set(socket.id, invoice.id);
this.invoiceSocket.set(invoice.id, socket);
socket.emit('subscribe', true);
}
});
});
}
getSocketById(id: string) {
return this.idSocket.get(id);
}
async getInvoiceBySocket(socketId: string) {
const invoiceId = this.socketInvoice.get(socketId);
return await Invoice.findById(invoiceId);
}
getSocketByInvoice(invoice: IInvoice) {
return this.invoiceSocket.get(invoice.id);
}
}

View File

@@ -7,9 +7,16 @@ export enum CryptoUnits {
MONERO = 'XMR' MONERO = 'XMR'
} }
export function findCryptoBySymbol(symbol: string): string | null {
for (let coin in CryptoUnits) {
if (CryptoUnits[coin] === symbol.toUpperCase()) return coin;
}
return null;
}
export enum FiatUnits { export enum FiatUnits {
USD = 'USD', USD = 'USD',
EUR = 'EURO' EUR = 'EUR'
} }
export enum PaymentStatus { export enum PaymentStatus {
@@ -18,13 +25,23 @@ export enum PaymentStatus {
*/ */
PENDING = 0, PENDING = 0,
/**
* The payment has been paid, but not completly.
*/
PARTIALLY = 1,
/** /**
* The payment has been made but it's not yet confirmed. * The payment has been made but it's not yet confirmed.
*/ */
UNCONFIRMED = 1, UNCONFIRMED = 2,
/** /**
* The payment is completed and the crypto is now available. * The payment is completed and the crypto is now available.
*/ */
DONE = 2 DONE = 3,
/**
* The payment has been cancelled by the user.
*/
CANCELLED = 4
} }

View File

@@ -8,19 +8,30 @@ export interface ICart {
quantity: number; quantity: number;
} }
export interface IPaymentMethod {
method: CryptoUnits;
amount: number
}
export interface IInvoice extends Document { export interface IInvoice extends Document {
selector: string;
// Available payment methods // Available payment methods
// [btc, xmr, eth, doge] // [{ method: 'btc', amount: 0.0000105 }]
paymentMethods: CryptoUnits[]; paymentMethods: IPaymentMethod[];
// 1Kss3e9iPB9vTgWJJZ1SZNkkFKcFJXPz9t // 1Kss3e9iPB9vTgWJJZ1SZNkkFKcFJXPz9t
receiveAddress: string; receiveAddress: string;
paidWith?: CryptoUnits; paidWith?: CryptoUnits;
// Already paid amount, in case that not the entire amount was paid with once.
// 0.000013
paid?: number;
// Is set when invoice got paid // Is set when invoice got paid
// 3b38c3a215d4e7981e1516b2dcbf76fca58911274d5d55b3d615274d6e10f2c1 // 3b38c3a215d4e7981e1516b2dcbf76fca58911274d5d55b3d615274d6e10f2c1
transcationHash?: string; transcationHashes?: string[];
cart?: ICart[]; cart?: ICart[];
totalPrice?: number; totalPrice?: number;

View File

@@ -1,6 +1,7 @@
import { NativeError, Schema, SchemaTypes } from 'mongoose'; import { Schema } from 'mongoose';
import { socketManager } from '../../app';
import { CryptoUnits, FiatUnits, PaymentStatus } from '../../helper/types'; import { CryptoUnits, FiatUnits, PaymentStatus } from '../../helper/types';
import { IInvoice } from './invoice.interface'; import { ICart, IInvoice } from './invoice.interface';
const urlRegex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/ const urlRegex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/
@@ -9,16 +10,23 @@ const schemaCart = new Schema({
name: { type: String, trim: true, required: true }, name: { type: String, trim: true, required: true },
image: { type: String, match: urlRegex, required: true }, image: { type: String, match: urlRegex, required: true },
quantity: { type: Number, default: 1 } quantity: { type: Number, default: 1 }
}) }, { _id: false });
const schemaPaymentMethods = new Schema({
method: { type: String, enum: Object.values(CryptoUnits), required: true },
amount: { type: Number, required: false }
}, { _id: false });
const schemaInvoice = new Schema({ const schemaInvoice = new Schema({
paymentMethods: [{ type: String, enum: Object.values(CryptoUnits), default: [CryptoUnits.BITCOIN], required: true }], selector: { type: String, length: 128, required: true },
paymentMethods: [{ type: schemaPaymentMethods, required: true }],
receiveAddress: { type: String, required: true }, receiveAddress: { type: String, required: true },
paidWith: { type: String, enum: CryptoUnits }, paidWith: { type: String, enum: CryptoUnits },
transcationHash: { type: String, required: false }, paid: { type: Number, default: 0 },
transcationHashes: [{ type: String, required: false }],
cart: [{ type: schemaCart, required: false }], cart: [{ type: schemaCart, required: false }],
totalPrice: { type: Number, required: false }, totalPrice: { type: Number, required: false },
currency: { type: String, enum: Object.values(FiatUnits), required: false }, currency: { type: String, enum: Object.values(FiatUnits), required: true },
dueBy: { type: Number, required: true }, dueBy: { type: Number, required: true },
status: { type: Number, enum: Object.values(PaymentStatus), default: PaymentStatus.PENDING }, status: { type: Number, enum: Object.values(PaymentStatus), default: PaymentStatus.PENDING },
email: { type: String, required: false }, email: { type: String, required: false },
@@ -31,8 +39,15 @@ const schemaInvoice = new Schema({
versionKey: false versionKey: false
}); });
schemaInvoice.pre('validate', function(next) {
let self = this as IInvoice;
self.currency = FiatUnits[self.currency];
next();
});
// Validate values // Validate values
schemaInvoice.post('validate', function (res, next) { schemaInvoice.post('validate', function (doc, next) {
let self = this as IInvoice; let self = this as IInvoice;
// If cart is undefined and price too, error. // If cart is undefined and price too, error.
@@ -40,19 +55,27 @@ schemaInvoice.post('validate', function (res, next) {
next(new Error('Either cart or price has to be defined!')); next(new Error('Either cart or price has to be defined!'));
return; return;
} }
next();
});
// If cart is provided, calculate price. schemaInvoice.post('save', function(doc, next) {
if (self.cart !== undefined && self.totalPrice === undefined) { let self = this as IInvoice;
let totalPrice = 0;
for (let i = 0; i < self.cart.length; i++) {
const item = self.cart[i];
totalPrice += item.price * item.quantity;
}
self.set({ totalPrice }); if (socketManager.getSocketByInvoice(self) === undefined) return;
} socketManager.getSocketByInvoice(self).emit('status', self.status);
next(); next();
}) })
export function calculateCart(cart: ICart[]): number {
let totalPrice = 0;
for (let i = 0; i < cart.length; i++) {
const item = cart[i];
totalPrice += item.price * item.quantity;
}
return totalPrice;
}
export { schemaInvoice } export { schemaInvoice }

View File

@@ -4,7 +4,7 @@ import { MW_User } from "../controllers/user";
const invoiceRouter = Router() const invoiceRouter = Router()
invoiceRouter.get('/:id', getInvoice); invoiceRouter.get('/:selector', getInvoice);
invoiceRouter.get('/', MW_User, getInvoice); invoiceRouter.get('/', MW_User, getInvoice);
invoiceRouter.post('/', MW_User, createInvoice); invoiceRouter.post('/', MW_User, createInvoice);