Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ebc542be6 | ||
|
|
f9f3624305 | ||
|
|
78e56ee75e | ||
|
|
1017448362 |
55
.github/workflows/main_ci.yml
vendored
55
.github/workflows/main_ci.yml
vendored
@ -1,55 +0,0 @@
|
|||||||
name: Run Tests
|
|
||||||
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
services:
|
|
||||||
btcpay:
|
|
||||||
image: junderw/btcpay-client-test-server@sha256:2ab65051329e0250cd7d5ff3f3182a95d81228e469b518beb570a288f313e5c1
|
|
||||||
ports:
|
|
||||||
- 49392:49392
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
registry-url: https://registry.npmjs.org/
|
|
||||||
- run: npm ci
|
|
||||||
- run: npm test
|
|
||||||
coverage-check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
services:
|
|
||||||
btcpay:
|
|
||||||
image: junderw/btcpay-client-test-server@sha256:2ab65051329e0250cd7d5ff3f3182a95d81228e469b518beb570a288f313e5c1
|
|
||||||
ports:
|
|
||||||
- 49392:49392
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
registry-url: https://registry.npmjs.org/
|
|
||||||
- run: npm ci
|
|
||||||
- run: npm run coverage
|
|
||||||
lint-check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
registry-url: https://registry.npmjs.org/
|
|
||||||
- run: npm ci
|
|
||||||
- run: npm run lint
|
|
||||||
format-check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
registry-url: https://registry.npmjs.org/
|
|
||||||
- run: npm ci
|
|
||||||
- run: npm run format:ci
|
|
||||||
64
.gitignore
vendored
64
.gitignore
vendored
@ -1,62 +1,2 @@
|
|||||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
# Compiled source #
|
|
||||||
###################
|
|
||||||
*.com
|
|
||||||
*.class
|
|
||||||
*.dll
|
|
||||||
*.exe
|
|
||||||
*.o
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Packages #
|
|
||||||
############
|
|
||||||
# it's better to unpack these files and commit the raw source
|
|
||||||
# git has its own built in compression methods
|
|
||||||
*.7z
|
|
||||||
*.dmg
|
|
||||||
*.gz
|
|
||||||
*.iso
|
|
||||||
*.jar
|
|
||||||
*.rar
|
|
||||||
*.tar
|
|
||||||
*.zip
|
|
||||||
|
|
||||||
# Logs and databases #
|
|
||||||
######################
|
|
||||||
*.log
|
|
||||||
*.sql
|
|
||||||
*.sqlite
|
|
||||||
|
|
||||||
# OS generated files #
|
|
||||||
######################
|
|
||||||
.DS_Store
|
|
||||||
.DS_Store?
|
|
||||||
._*
|
|
||||||
.Spotlight-V100
|
|
||||||
.Trashes
|
|
||||||
ehthumbs.db
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Environment variables #
|
|
||||||
#########################
|
|
||||||
.env
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
# Node.js #
|
|
||||||
################
|
|
||||||
/node_modules
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# IDE #
|
|
||||||
#######
|
|
||||||
.vscode
|
|
||||||
.idea
|
|
||||||
|
|
||||||
dist/
|
|
||||||
coverage/
|
|
||||||
39
README.md
39
README.md
@ -1,20 +1,20 @@
|
|||||||
> :warning: This package is deprecated, BTCPay Server is exposing a new, more complete and easy to use API called `Greenfield`. [Check the doc](https://docs.btcpayserver.org/Development/GreenFieldExample/)
|
|
||||||
|
|
||||||
# node-btcpay
|
# node-btcpay
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
```shell
|
```shell
|
||||||
npm i btcpay
|
npm install https://github.com/tanjalo/node-btcpay
|
||||||
```
|
```
|
||||||
|
|
||||||
## Private key generation
|
## Private key generation
|
||||||
* Generate and save private key:
|
* Generate and save private key:
|
||||||
```bash
|
```js
|
||||||
$ node -p "require('btcpay').crypto.generate_keypair().getPrivate('hex')"
|
const btcpay = require('btcpay')
|
||||||
XXXXXXXXXXXXXXXXXXXXX
|
const privatekey = btcpay.crypto.generate_keypair().getPrivate('hex')
|
||||||
|
|
||||||
|
console.log(`PRIVATEKEY: ${privatekey}`)
|
||||||
```
|
```
|
||||||
|
|
||||||
Store the printed value in a safe place, e.g. environment variables
|
Store the value of "priv" in a save place, e.g. environment variables
|
||||||
|
|
||||||
## Pairing
|
## Pairing
|
||||||
|
|
||||||
@ -23,18 +23,20 @@ After generating your private key, you have to pair your client with your BTCPay
|
|||||||
* On BTCPay Server > Stores > Settings > Access Tokens > Create a new token, (leave PublicKey blank) > Request pairing
|
* On BTCPay Server > Stores > Settings > Access Tokens > Create a new token, (leave PublicKey blank) > Request pairing
|
||||||
* Copy pairing code:
|
* Copy pairing code:
|
||||||
* Pair client to server and save returned token:
|
* Pair client to server and save returned token:
|
||||||
|
```js
|
||||||
|
const btcpay = require('btcpay')
|
||||||
|
const keypair = btcpay.crypto.load_keypair(new Buffer.from(<PRIVATEKEY>, 'hex'))
|
||||||
|
const client = new btcpay.BTCPayClient(<BTCPAYURL>, btcpay.crypto.load_keypair(Buffer.from(<PRIVATEKEY>, 'hex')))
|
||||||
|
|
||||||
```bash
|
// Pair client to server
|
||||||
# Replace the BTCPAY_XXX envirnoment variables with your values and run:
|
client
|
||||||
|
.pair_client(<PAIRINGCODE>)
|
||||||
$ [space] BTCPAY_URL=https://mydomain.com/ BTCPAY_KEY=... BTCPAY_PAIRCODE=... node -e "const btcpay=require('btcpay'); new btcpay.BTCPayClient(process.env.BTCPAY_URL, btcpay.crypto.load_keypair(Buffer.from(process.env.BTCPAY_KEY, 'hex'))).pair_client(process.env.BTCPAY_PAIRCODE).then(console.log).catch(console.error)"
|
.then(res => console.log(res))
|
||||||
|
.catch(err => console.log(err))
|
||||||
# (prepend the line with a space to prevent BTCPAY_KEY from being saved to your bash history)
|
|
||||||
|
|
||||||
>>> { merchant: 'XXXXXX' }
|
>>> { merchant: 'XXXXXX' }
|
||||||
```
|
```
|
||||||
|
Store the value of "merchant" in a save place, e.g. environment variables
|
||||||
Store the value of "merchant" in a safe place, e.g. environment variables
|
|
||||||
|
|
||||||
## Recreating a client
|
## Recreating a client
|
||||||
After pairing your client to the store, you can recreate the client as needed and use it in your code
|
After pairing your client to the store, you can recreate the client as needed and use it in your code
|
||||||
@ -43,17 +45,16 @@ const btcpay = require('btcpay')
|
|||||||
const keypair = btcpay.crypto.load_keypair(new Buffer.from(<PRIVATEKEY>, 'hex'))
|
const keypair = btcpay.crypto.load_keypair(new Buffer.from(<PRIVATEKEY>, 'hex'))
|
||||||
|
|
||||||
// Recreate client
|
// Recreate client
|
||||||
const client = new btcpay.BTCPayClient(<BTCPAYURL>, keypair, {merchant: <MERCHANT>})
|
const client = new btcpay.BTCPayClient(<BCTPAYURL>, keypair, {merchant: <MERCHANT>})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get rates
|
### Get rates
|
||||||
Fetches current rates from BitcoinAverage (using your BTCPayServer)
|
Fetches current rates from BitcoinAverage (using your BTCPayServer)
|
||||||
```js
|
```js
|
||||||
client.get_rates(['BTC_USD'], <STOREID>)
|
client.get_rates('BTC_USD')
|
||||||
.then(rates => console.log(rates))
|
.then(rates => console.log(rates))
|
||||||
.catch(err => console.log(err))
|
.catch(err => console.log(err))
|
||||||
```
|
```
|
||||||
The first argument accepts a comma-separated list of currency pair.
|
|
||||||
|
|
||||||
### Create invoice
|
### Create invoice
|
||||||
See [BitPay Invoice API documentation](https://bitpay.com/api#resource-Invoices)
|
See [BitPay Invoice API documentation](https://bitpay.com/api#resource-Invoices)
|
||||||
@ -68,4 +69,4 @@ client.create_invoice({price: 20, currency: 'USD'})
|
|||||||
client.get_invoice(<invoice-id>)
|
client.get_invoice(<invoice-id>)
|
||||||
.then(invoice => console.log(invoice.status))
|
.then(invoice => console.log(invoice.status))
|
||||||
.catch(err => console.log(err))
|
.catch(err => console.log(err))
|
||||||
```
|
```
|
||||||
161
client.js
Normal file
161
client.js
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
let _ = require('underscore')
|
||||||
|
let Promise = require('bluebird')
|
||||||
|
let crypto = require('./cryptography')
|
||||||
|
let qs = require('querystring')
|
||||||
|
let rp = require('request-promise')
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: peer certificate verification
|
||||||
|
function BTCPayClient(host, keypair, tokens) {
|
||||||
|
this.host = host
|
||||||
|
this.kp = keypair
|
||||||
|
this.tokens = tokens == undefined ? {} : tokens
|
||||||
|
this.client_id = crypto.get_sin_from_key(keypair)
|
||||||
|
this.user_agent = 'node-btcpay'
|
||||||
|
this.options = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'X-Accept-Version': '2.0.0'
|
||||||
|
},
|
||||||
|
json: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BTCPayClient.prototype._create_signed_headers = function (uri, payload) {
|
||||||
|
return {
|
||||||
|
'X-Identity': Buffer.from(this.kp.getPublic().encodeCompressed()).toString('hex'),
|
||||||
|
'X-Signature': crypto.sign(uri + payload, this.kp).toString('hex')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BTCPayClient.prototype._signed_get_request = function (path, params, token) {
|
||||||
|
let _token = token ? token : _.values(this.tokens)[0]
|
||||||
|
let _params = params ? params : {}
|
||||||
|
_params['token'] = _token
|
||||||
|
|
||||||
|
let _uri = this.host + path
|
||||||
|
let _payload = '?' + qs.stringify(_params)
|
||||||
|
let _options = JSON.parse(JSON.stringify(this.options))
|
||||||
|
|
||||||
|
_.extend(_options.headers, this._create_signed_headers(_uri, _payload))
|
||||||
|
_options['uri'] = _uri
|
||||||
|
_options['qs'] = _params
|
||||||
|
|
||||||
|
return rp.get(_options).then(resp => {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
resolve(resp['data'])
|
||||||
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BTCPayClient.prototype._signed_post_request = function (path, payload, token) {
|
||||||
|
let _token = token ? token : _.values(this.tokens)[0]
|
||||||
|
payload['token'] = _token
|
||||||
|
|
||||||
|
let _uri = this.host + path
|
||||||
|
let _payload = JSON.stringify(payload)
|
||||||
|
let _options = JSON.parse(JSON.stringify(this.options))
|
||||||
|
|
||||||
|
_.extend(_options.headers, this._create_signed_headers(_uri, _payload))
|
||||||
|
_options['uri'] = _uri
|
||||||
|
_options['body'] = payload
|
||||||
|
|
||||||
|
return rp.post(_options).then(resp => {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
resolve(resp['data'])
|
||||||
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BTCPayClient.prototype._unsigned_request = function (path, payload) {
|
||||||
|
let _uri = this.host + path
|
||||||
|
let _options = JSON.parse(JSON.stringify(this.options))
|
||||||
|
|
||||||
|
if (payload) {
|
||||||
|
_options['uri'] = _uri
|
||||||
|
_options['body'] = payload
|
||||||
|
|
||||||
|
return rp.post(_options).then(resp => {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
resolve(resp['data'])
|
||||||
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
_options['uri'] = _uri
|
||||||
|
|
||||||
|
return rp.get(_options).then(resp => {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
resolve(resp['data'])
|
||||||
|
})
|
||||||
|
}).catch(err => {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BTCPayClient.prototype.get_rates = function (currencyPairs) {
|
||||||
|
let _params = {currencyPairs: currencyPairs}
|
||||||
|
return this._signed_get_request('/rates', _params)
|
||||||
|
}
|
||||||
|
|
||||||
|
BTCPayClient.prototype.create_invoice = function (payload, token) {
|
||||||
|
let re = new RegExp('^[A-Z]{3,3}$')
|
||||||
|
|
||||||
|
if (!re.test(payload['currency'])) {
|
||||||
|
throw 'Currency is invalid'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(parseFloat(payload['price']))) {
|
||||||
|
throw 'Price must be a float'
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._signed_post_request('/invoices', payload, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BTCPayClient.prototype.get_invoice = function (invoice_id, token) {
|
||||||
|
return this._signed_get_request('/invoices/' + invoice_id, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BTCPayClient.prototype.pair_client = function (code) {
|
||||||
|
let re = new RegExp('^\\w{7,7}$')
|
||||||
|
|
||||||
|
if (!re.test(code)) {
|
||||||
|
throw 'pairing code is not valid'
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = {id: this.client_id, pairingCode: code}
|
||||||
|
|
||||||
|
return this._unsigned_request('/tokens', payload).then(data => {
|
||||||
|
let _data = data[0]
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
let _res = {}
|
||||||
|
_res[_data.facade] = _data.token
|
||||||
|
|
||||||
|
resolve(_res)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BTCPayClient
|
||||||
45
cryptography.js
Normal file
45
cryptography.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
let crypto = require('crypto')
|
||||||
|
let EC = require('elliptic').ec,
|
||||||
|
ec = new EC('secp256k1');
|
||||||
|
let bs58 = require('bs58')
|
||||||
|
const BN = require('bn.js')
|
||||||
|
|
||||||
|
let generate_keypair = function () {
|
||||||
|
let kp = ec.genKeyPair()
|
||||||
|
return kp
|
||||||
|
}
|
||||||
|
|
||||||
|
let load_keypair = function (buf) {
|
||||||
|
return ec.keyFromPrivate(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
let get_sin_from_key = function (kp) {
|
||||||
|
let pk = Buffer.from(kp.getPublic().encodeCompressed())
|
||||||
|
let version = get_version_from_compressed_key(pk)
|
||||||
|
let checksum = get_checksum_from_version(version)
|
||||||
|
return bs58.encode(Buffer.concat([version, checksum]))
|
||||||
|
}
|
||||||
|
|
||||||
|
let sign = function (message, kp) {
|
||||||
|
let digest = crypto.createHash('sha256').update(message).digest()
|
||||||
|
return Buffer.from(kp.sign(digest).toDER())
|
||||||
|
}
|
||||||
|
|
||||||
|
let get_version_from_compressed_key = function (pk) {
|
||||||
|
let sh2 = crypto.createHash('sha256').update(pk).digest()
|
||||||
|
let rp = crypto.createHash('ripemd160').update(sh2).digest()
|
||||||
|
|
||||||
|
return Buffer.concat([Buffer.from('0F', 'hex'), Buffer.from('02', 'hex'), rp])
|
||||||
|
}
|
||||||
|
|
||||||
|
let get_checksum_from_version = function (version) {
|
||||||
|
let h1 = crypto.createHash('sha256').update(version).digest()
|
||||||
|
let h2 = crypto.createHash('sha256').update(h1).digest()
|
||||||
|
|
||||||
|
return h2.slice(0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.generate_keypair = generate_keypair
|
||||||
|
exports.load_keypair = load_keypair
|
||||||
|
exports.get_sin_from_key = get_sin_from_key
|
||||||
|
exports.sign = sign
|
||||||
@ -1,80 +0,0 @@
|
|||||||
FROM junderw/nbxplorer-client-test-server
|
|
||||||
MAINTAINER Jonathan Underwood
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
WORKDIR /root
|
|
||||||
|
|
||||||
# Install postgres
|
|
||||||
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main" >> /etc/apt/sources.list.d/pgdg.list
|
|
||||||
RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
|
|
||||||
RUN apt update && \
|
|
||||||
apt install -y \
|
|
||||||
postgresql-11 \
|
|
||||||
dotnet-sdk-3.1
|
|
||||||
|
|
||||||
RUN mkdir -p /pgsql/data
|
|
||||||
RUN chmod 700 /pgsql/data && \
|
|
||||||
chown postgres:postgres /pgsql/data
|
|
||||||
|
|
||||||
RUN su postgres -c "\
|
|
||||||
cd /pgsql/data; \
|
|
||||||
/usr/lib/postgresql/11/bin/pg_ctl initdb --pgdata=/pgsql/data; \
|
|
||||||
/usr/lib/postgresql/11/bin/postgres -D /pgsql/data -h 0.0.0.0 -i & \
|
|
||||||
sleep 5; \
|
|
||||||
psql -h 127.0.0.1 -c \"CREATE DATABASE btcpayserverregtest;\"; \
|
|
||||||
sleep 3; \
|
|
||||||
/usr/lib/postgresql/11/bin/pg_ctl stop --pgdata=/pgsql/data -m f"
|
|
||||||
|
|
||||||
# Install BTCPayServer
|
|
||||||
RUN git clone https://github.com/btcpayserver/btcpayserver.git && \
|
|
||||||
cd btcpayserver/ && \
|
|
||||||
git checkout 80e46db && \
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT=1 dotnet build -c Release BTCPayServer/BTCPayServer.csproj
|
|
||||||
|
|
||||||
# Re-install NBXplorer because I'm an idiot
|
|
||||||
RUN cd NBXplorer/ && \
|
|
||||||
git fetch origin && \
|
|
||||||
git checkout ce2f21f && \
|
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT=1 dotnet build -c Release NBXplorer/NBXplorer.csproj
|
|
||||||
|
|
||||||
RUN sed -i 's/generate 432/generatetoaddress 432 \$\(bitcoin-cli -regtest getnewaddress\)/g' /root/run_bitcoind_service.sh
|
|
||||||
|
|
||||||
RUN apt install -y \
|
|
||||||
libx11-6 libx11-xcb1 libxcb1 libxcb-dri3-0 libxcomposite1 \
|
|
||||||
libxcursor1 libxdamage1 libxi6 libxtst6 libnss3 libcups2 \
|
|
||||||
libxss1 libdrm2 libxrandr2 libgbm1 libasound2 libatk1.0-0 \
|
|
||||||
libatk-bridge2.0-0 libpangocairo-1.0-0 libgtk-3-0
|
|
||||||
|
|
||||||
COPY \
|
|
||||||
start_everything.sh \
|
|
||||||
start_btcpay.sh \
|
|
||||||
./
|
|
||||||
RUN chmod +x start_everything.sh && \
|
|
||||||
chmod +x start_btcpay.sh
|
|
||||||
|
|
||||||
RUN mkdir -p /root/registerAdmin && \
|
|
||||||
cd /root/registerAdmin && \
|
|
||||||
npm init -y && \
|
|
||||||
npm install puppeteer@3.0.1 btcpay@0.2.4
|
|
||||||
COPY \
|
|
||||||
registerAdmin.js \
|
|
||||||
./registerAdmin
|
|
||||||
|
|
||||||
RUN /root/start_everything.sh >/dev/null 2>&1 & \
|
|
||||||
sleep 20 && \
|
|
||||||
BTCPAY_IGNORE_SANDBOX_ERROR=1 node /root/registerAdmin/registerAdmin.js
|
|
||||||
|
|
||||||
RUN apt-get purge -y \
|
|
||||||
libx11-6 libx11-xcb1 libxcb1 libxcb-dri3-0 libxcomposite1 \
|
|
||||||
libxcursor1 libxdamage1 libxi6 libxtst6 libnss3 libcups2 \
|
|
||||||
libxss1 libdrm2 libxrandr2 libgbm1 libasound2 libatk1.0-0 \
|
|
||||||
libatk-bridge2.0-0 libpangocairo-1.0-0 libgtk-3-0 && \
|
|
||||||
apt-get autoremove -y && \
|
|
||||||
rm -rf /root/registerAdmin
|
|
||||||
|
|
||||||
ENTRYPOINT ["/root/start_everything.sh"]
|
|
||||||
|
|
||||||
EXPOSE 18271
|
|
||||||
EXPOSE 23828
|
|
||||||
EXPOSE 49392
|
|
||||||
EXPOSE 8080
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
# Docker Image
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ./docker
|
|
||||||
./build.sh btcpay-client-test-server
|
|
||||||
```
|
|
||||||
|
|
||||||
## OR Pull from docker hub
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker pull junderw/btcpay-client-test-server
|
|
||||||
```
|
|
||||||
|
|
||||||
## run on localhost then run tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# If you built it
|
|
||||||
docker run -d -p 49392:49392 btcpay-client-test-server
|
|
||||||
# OR, if you pulled from docker hub
|
|
||||||
docker run -d -p 49392:49392 junderw/btcpay-client-test-server
|
|
||||||
|
|
||||||
# Wait 6 seconds before running the tests
|
|
||||||
|
|
||||||
# Install deps
|
|
||||||
npm install
|
|
||||||
# Run tests
|
|
||||||
npm test
|
|
||||||
```
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
|
||||||
TEMP=$1-temp
|
|
||||||
CONT=tempcont$(date +%s)
|
|
||||||
|
|
||||||
docker build $DIR -t $TEMP
|
|
||||||
docker run --name $CONT $TEMP &
|
|
||||||
sleep 120
|
|
||||||
docker exec $CONT bash -c "bitcoin-cli -regtest sendtoaddress \$(cat /root/btcpay.address.p2wpkh) 0.1337"
|
|
||||||
docker exec $CONT bash -c "bitcoin-cli -regtest sendtoaddress \$(cat /root/btcpay.address.p2shp2wpkh) 0.1337"
|
|
||||||
docker exec $CONT bash -c "bitcoin-cli -regtest generatetoaddress 6 mwhw4fySPWHZ4CUT1QHXWqAdE2wj9pJsa4"
|
|
||||||
sleep 20
|
|
||||||
docker exec $CONT bash -c "kill \$(pgrep BTCPayServer)"
|
|
||||||
docker exec $CONT su postgres -c "/usr/lib/postgresql/11/bin/pg_ctl stop --pgdata=/pgsql/data -m f"
|
|
||||||
docker exec $CONT bash -c "kill \$(pgrep NBXplorer)"
|
|
||||||
docker exec $CONT bash -c "kill \$(pgrep node)"
|
|
||||||
docker exec $CONT bitcoin-cli -regtest stop
|
|
||||||
docker exec $CONT bash -c "sed -i 's/\/usr\/bin\/bitcoin-cli -regtest generatetoaddress 432 mwhw4fySPWHZ4CUT1QHXWqAdE2wj9pJsa4//g' /root/run_bitcoind_service.sh"
|
|
||||||
docker exec $CONT bash -c "sed -i 's/sleep 2//g' /root/run_bitcoind_service.sh"
|
|
||||||
docker exec $CONT bash -c "sed -i 's/\/cookie/\/tokens/g' /root/stopAndCookie/stopAndCookie.js"
|
|
||||||
docker exec $CONT bash -c "sed -i 's/\/datadir\/RegTest\/.cookie/\/root\/btcpaytokens/g' /root/stopAndCookie/stopAndCookie.js"
|
|
||||||
docker exec $CONT bash -c "sed -i 's/sleep infinity/node \/root\/stopAndCookie\/stopAndCookie.js \&\nsleep infinity/g' /root/start_everything.sh"
|
|
||||||
docker stop $CONT
|
|
||||||
docker commit $CONT $TEMP
|
|
||||||
$DIR/build_stage2.sh $1
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
|
||||||
TEMP=$1-temp
|
|
||||||
CONT=tempcont$(date +%s)
|
|
||||||
|
|
||||||
# Start up the temp image and wait for BTCPayServer to calm down, then shutdown
|
|
||||||
docker run --name $CONT $TEMP &
|
|
||||||
sleep 15
|
|
||||||
docker exec $CONT bash -c "kill \$(pgrep BTCPayServer)"
|
|
||||||
docker exec $CONT su postgres -c "/usr/lib/postgresql/11/bin/pg_ctl stop --pgdata=/pgsql/data -m f"
|
|
||||||
docker exec $CONT bash -c "kill \$(pgrep NBXplorer)"
|
|
||||||
docker exec $CONT bash -c "kill \$(pgrep node)"
|
|
||||||
docker exec $CONT bitcoin-cli -regtest stop
|
|
||||||
|
|
||||||
# New checkout (for testing)
|
|
||||||
# docker exec $CONT bash -c "cd /root/btcpayserver; \
|
|
||||||
# git fetch origin; \
|
|
||||||
# git checkout 79c70b3;"
|
|
||||||
|
|
||||||
# Replace login files
|
|
||||||
docker exec $CONT bash -c "sed -i 's/input asp-for=\"Email\"/input asp-for=\"Email\" value=\"test\@example.com\"/g' /root/btcpayserver/BTCPayServer/Views/Account/Login.cshtml"
|
|
||||||
docker exec $CONT bash -c "sed -i 's/input asp-for=\"Password\"/input asp-for=\"Password\" value=\"satoshinakamoto\"/g' /root/btcpayserver/BTCPayServer/Views/Account/Login.cshtml"
|
|
||||||
# Rebuild BTCPayServer
|
|
||||||
docker exec $CONT bash -c "cd /root/btcpayserver; DOTNET_CLI_TELEMETRY_OPTOUT=1 dotnet build -c Release BTCPayServer/BTCPayServer.csproj"
|
|
||||||
sleep 2
|
|
||||||
docker stop $CONT
|
|
||||||
docker commit $CONT $1
|
|
||||||
@ -1,237 +0,0 @@
|
|||||||
const puppeteer = require('puppeteer')
|
|
||||||
const fs = require('fs')
|
|
||||||
const btcpay = require('btcpay')
|
|
||||||
|
|
||||||
const IGNORE_SANDBOX_ERROR = process.env['BTCPAY_IGNORE_SANDBOX_ERROR'];
|
|
||||||
const URL = 'http://127.0.0.1:49392';
|
|
||||||
const STORENAME = 'Test Store for testing';
|
|
||||||
const USER_NAME = 'test@example.com';
|
|
||||||
const PASSWORD = 'satoshinakamoto';
|
|
||||||
const WINDOW_WIDTH = 1920
|
|
||||||
const WINDOW_HEIGHT = 1080
|
|
||||||
const HEADLESS = true
|
|
||||||
|
|
||||||
// const sleep = ms => new Promise(r => setTimeout(r,ms))
|
|
||||||
|
|
||||||
function writeAddress(address, type) {
|
|
||||||
fs.writeFileSync('/root/btcpay.address.' + type, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeTokens(tokens) {
|
|
||||||
fs.writeFileSync('/root/btcpaytokens', tokens)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getElValue(page, qs) {
|
|
||||||
const idElement = await page.$$(qs);
|
|
||||||
return idElement[0]
|
|
||||||
.getProperty('value')
|
|
||||||
.then(v => v.jsonValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const browser = await puppeteer.launch({
|
|
||||||
headless: HEADLESS,
|
|
||||||
args: ['--window-size=' + WINDOW_WIDTH + ',' + WINDOW_HEIGHT],
|
|
||||||
}).then(
|
|
||||||
v => v, // if success, passthrough
|
|
||||||
// if error, check for env and ignore sandbox and warn.
|
|
||||||
err => {
|
|
||||||
if (IGNORE_SANDBOX_ERROR === '1') {
|
|
||||||
console.warn(
|
|
||||||
'WARNING!!! Error occurred, Chromium will be started ' +
|
|
||||||
"without sandbox. This won't guarantee success.",
|
|
||||||
);
|
|
||||||
return puppeteer.launch({
|
|
||||||
headless: HEADLESS,
|
|
||||||
ignoreDefaultArgs: ['--disable-extensions'],
|
|
||||||
args: [
|
|
||||||
'--no-sandbox',
|
|
||||||
'--disable-setuid-sandbox',
|
|
||||||
'--window-size=' + WINDOW_WIDTH + ',' + WINDOW_HEIGHT,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.warn(
|
|
||||||
'If "No usable sandbox!" error, retry test with ' +
|
|
||||||
'BTCPAY_IGNORE_SANDBOX_ERROR=1',
|
|
||||||
);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const page = (await browser.pages())[0];
|
|
||||||
await page.setViewport({ width: WINDOW_WIDTH, height: WINDOW_HEIGHT });
|
|
||||||
|
|
||||||
await page.goto(URL + '/Account/Register');
|
|
||||||
|
|
||||||
// Set up admin account
|
|
||||||
await page.type('#Email', USER_NAME);
|
|
||||||
await page.type('#Password', PASSWORD);
|
|
||||||
await page.type('#ConfirmPassword', PASSWORD);
|
|
||||||
await page.click('#RegisterButton');
|
|
||||||
await page.waitForSelector('#Stores');
|
|
||||||
|
|
||||||
// On main screen, setup first store
|
|
||||||
await page.click('#Stores');
|
|
||||||
await page.waitForSelector('#CreateStore');
|
|
||||||
await page.click('#CreateStore');
|
|
||||||
await page.type('#Name', STORENAME);
|
|
||||||
await page.click('#Create');
|
|
||||||
await page.waitForSelector('#Id');
|
|
||||||
const STORE_ID = await getElValue(page, '#Id');
|
|
||||||
|
|
||||||
// Add new wallet
|
|
||||||
await page.click('#ModifyBTC');
|
|
||||||
await page.waitForSelector('#import-from-btn');
|
|
||||||
await page.click('#import-from-btn');
|
|
||||||
await page.waitForSelector('#nbxplorergeneratewalletbtn');
|
|
||||||
await page.click('#nbxplorergeneratewalletbtn');
|
|
||||||
await page.waitForSelector('#btn-generate');
|
|
||||||
await page.evaluate(() => {
|
|
||||||
document.querySelector('#SavePrivateKeys').click();
|
|
||||||
document.querySelector('#btn-generate').click();
|
|
||||||
});
|
|
||||||
await page.waitForSelector('#confirm');
|
|
||||||
await page.evaluate(() => {
|
|
||||||
document.querySelector('#confirm').click();
|
|
||||||
document.querySelector('#submit').click();
|
|
||||||
});
|
|
||||||
await page.waitForSelector('#PayJoinEnabled');
|
|
||||||
|
|
||||||
// Enable PayJoin
|
|
||||||
await page.evaluate(() => {
|
|
||||||
document.querySelector('#PayJoinEnabled').click();
|
|
||||||
document.querySelector('#Save').click();
|
|
||||||
});
|
|
||||||
const alertQS = 'div.alert.alert-success.alert-dismissible'
|
|
||||||
await page.waitForSelector(alertQS);
|
|
||||||
|
|
||||||
// Get first address and write to disk
|
|
||||||
await page.click('#Wallets');
|
|
||||||
const firstManageLinkQS = 'table.table.table-sm.table-responsive-md > tbody > ' +
|
|
||||||
'tr:nth-of-type(1) > td:nth-of-type(4) > a:nth-of-type(1)'
|
|
||||||
await page.waitForSelector(firstManageLinkQS);
|
|
||||||
await page.click(firstManageLinkQS);
|
|
||||||
await page.waitForSelector('#WalletReceive');
|
|
||||||
await page.click('#WalletReceive');
|
|
||||||
await page.waitForSelector('#generateButton');
|
|
||||||
await page.click('#generateButton');
|
|
||||||
await page.waitForSelector('#vue-address');
|
|
||||||
const address = await getElValue(page, '#vue-address');
|
|
||||||
writeAddress(address, 'p2wpkh');
|
|
||||||
|
|
||||||
// On main screen, setup second store
|
|
||||||
await page.goto(URL + '/stores');
|
|
||||||
await page.waitForSelector('#CreateStore');
|
|
||||||
await page.click('#CreateStore');
|
|
||||||
await page.type('#Name', STORENAME + ' 2');
|
|
||||||
await page.click('#Create');
|
|
||||||
await page.waitForSelector('#Id');
|
|
||||||
const STORE_ID2 = await getElValue(page, '#Id');
|
|
||||||
|
|
||||||
// Add new wallet
|
|
||||||
await page.click('#ModifyBTC');
|
|
||||||
await page.waitForSelector('#import-from-btn');
|
|
||||||
await page.click('#import-from-btn');
|
|
||||||
await page.waitForSelector('#nbxplorergeneratewalletbtn');
|
|
||||||
await page.click('#nbxplorergeneratewalletbtn');
|
|
||||||
await page.waitForSelector('#btn-generate');
|
|
||||||
await page.evaluate(() => {
|
|
||||||
document.querySelector('#ScriptPubKeyType').value = 'SegwitP2SH';
|
|
||||||
document.querySelector('#SavePrivateKeys').click();
|
|
||||||
document.querySelector('#btn-generate').click();
|
|
||||||
});
|
|
||||||
await page.waitForSelector('#confirm');
|
|
||||||
await page.evaluate(() => {
|
|
||||||
document.querySelector('#confirm').click();
|
|
||||||
document.querySelector('#submit').click();
|
|
||||||
});
|
|
||||||
await page.waitForSelector('#PayJoinEnabled');
|
|
||||||
|
|
||||||
// Enable PayJoin
|
|
||||||
await page.evaluate(() => {
|
|
||||||
document.querySelector('#PayJoinEnabled').click();
|
|
||||||
document.querySelector('#Save').click();
|
|
||||||
});
|
|
||||||
await page.waitForSelector(alertQS);
|
|
||||||
|
|
||||||
// Get first address and write to disk
|
|
||||||
await page.click('#Wallets');
|
|
||||||
const secondManageLinkQS = 'table.table.table-sm.table-responsive-md > tbody > ' +
|
|
||||||
'tr:nth-of-type(2) > td:nth-of-type(4) > a:nth-of-type(1)'
|
|
||||||
await page.waitForSelector(secondManageLinkQS);
|
|
||||||
await page.click(secondManageLinkQS);
|
|
||||||
await page.waitForSelector('#WalletReceive');
|
|
||||||
await page.click('#WalletReceive');
|
|
||||||
await page.waitForSelector('#generateButton');
|
|
||||||
await page.click('#generateButton');
|
|
||||||
await page.waitForSelector('#vue-address');
|
|
||||||
const address2 = await getElValue(page, '#vue-address');
|
|
||||||
writeAddress(address2, 'p2shp2wpkh');
|
|
||||||
|
|
||||||
const tokens = {};
|
|
||||||
|
|
||||||
// Get token for store 1
|
|
||||||
await page.goto(URL + '/stores/' + STORE_ID + '/Tokens/Create');
|
|
||||||
await page.waitForSelector('input#Label');
|
|
||||||
await page.waitForSelector('[type="submit"]');
|
|
||||||
|
|
||||||
await page.type('#Label', 'token1');
|
|
||||||
await page.click('[type="submit"]');
|
|
||||||
await page.waitForSelector('button[type="submit"]');
|
|
||||||
await page.click('[type="submit"]');
|
|
||||||
await page.waitForSelector('div.alert.alert-success.alert-dismissible');
|
|
||||||
const contents1 = await page.evaluate(() => {
|
|
||||||
const el = document.querySelector(
|
|
||||||
'div.alert.alert-success.alert-dismissible',
|
|
||||||
);
|
|
||||||
if (el === null) return '';
|
|
||||||
return el.innerHTML;
|
|
||||||
});
|
|
||||||
const pairingCode1 = (contents1.match(
|
|
||||||
/Server initiated pairing code: (\S{7})/,
|
|
||||||
) || [])[1];
|
|
||||||
const kp1 = btcpay.crypto.generate_keypair()
|
|
||||||
const client1 = new btcpay.BTCPayClient(URL, kp1)
|
|
||||||
const token1 = await client1.pair_client(pairingCode1)
|
|
||||||
tokens.p2wpkh = token1;
|
|
||||||
|
|
||||||
// Get token for store 2
|
|
||||||
await page.goto(URL + '/stores/' + STORE_ID2 + '/Tokens/Create');
|
|
||||||
await page.waitForSelector('input#Label');
|
|
||||||
await page.waitForSelector('[type="submit"]');
|
|
||||||
|
|
||||||
await page.type('#Label', 'token2');
|
|
||||||
await page.click('[type="submit"]');
|
|
||||||
await page.waitForSelector('button[type="submit"]');
|
|
||||||
await page.click('[type="submit"]');
|
|
||||||
await page.waitForSelector('div.alert.alert-success.alert-dismissible');
|
|
||||||
const contents2 = await page.evaluate(() => {
|
|
||||||
const el = document.querySelector(
|
|
||||||
'div.alert.alert-success.alert-dismissible',
|
|
||||||
);
|
|
||||||
if (el === null) return '';
|
|
||||||
return el.innerHTML;
|
|
||||||
});
|
|
||||||
const pairingCode2 = (contents2.match(
|
|
||||||
/Server initiated pairing code: (\S{7})/,
|
|
||||||
) || [])[1];
|
|
||||||
const kp2 = btcpay.crypto.generate_keypair()
|
|
||||||
const client2 = new btcpay.BTCPayClient(URL, kp2)
|
|
||||||
const token2 = await client2.pair_client(pairingCode2)
|
|
||||||
tokens.p2shp2wpkh = token2;
|
|
||||||
tokens.privateKeys = {
|
|
||||||
p2wpkh: kp1.getPrivate('hex'),
|
|
||||||
p2shp2wpkh: kp2.getPrivate('hex'),
|
|
||||||
}
|
|
||||||
writeTokens(JSON.stringify(tokens))
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
||||||
.then(() => {
|
|
||||||
process.exit(0);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err)
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cd /root/btcpayserver
|
|
||||||
|
|
||||||
export BTCPAY_POSTGRES="User ID=postgres;Host=127.0.0.1;Port=5432;Database=btcpayserverregtest"
|
|
||||||
export BTCPAY_NETWORK=regtest
|
|
||||||
export BTCPAY_BIND=0.0.0.0:49392
|
|
||||||
export BTCPAY_ROOTPATH=/
|
|
||||||
export BTCPAY_CHAINS=btc
|
|
||||||
export BTCPAY_BTCEXPLORERURL=http://127.0.0.1:23828
|
|
||||||
export BTCPAY_BTCEXPLORERCOOKIEFILE=/datadir/RegTest/.cookie
|
|
||||||
|
|
||||||
# Run NBXplorer
|
|
||||||
dotnet run --no-launch-profile --no-build -c Release -p "BTCPayServer/BTCPayServer.csproj" -- $@
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# trap ctrl-c and call ctrl_c()
|
|
||||||
trap ctrl_c INT
|
|
||||||
|
|
||||||
function ctrl_c() {
|
|
||||||
echo "** Trapped CTRL-C"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run regtest app for using regtest-client
|
|
||||||
/root/run_regtest_app.sh &
|
|
||||||
disown
|
|
||||||
|
|
||||||
# Run NBXplorer
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
|
||||||
$DIR/start_nbx.sh &
|
|
||||||
disown
|
|
||||||
|
|
||||||
# Run postgres
|
|
||||||
chmod 777 /root
|
|
||||||
su postgres -c "/usr/lib/postgresql/11/bin/postgres -D /pgsql/data -h 0.0.0.0 -i" &
|
|
||||||
disown
|
|
||||||
sleep 5
|
|
||||||
|
|
||||||
# Run BTCPayServer
|
|
||||||
$DIR/start_btcpay.sh &
|
|
||||||
disown
|
|
||||||
|
|
||||||
sleep infinity
|
|
||||||
5
index.js
Normal file
5
index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
let crypto = require('./cryptography')
|
||||||
|
let BTCPayClient = require('./client')
|
||||||
|
|
||||||
|
exports.crypto = crypto
|
||||||
|
exports.BTCPayClient = BTCPayClient
|
||||||
31
jest.json
31
jest.json
@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"ts",
|
|
||||||
"js",
|
|
||||||
"json"
|
|
||||||
],
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.ts$": "ts-jest"
|
|
||||||
},
|
|
||||||
"testEnvironment" : "node",
|
|
||||||
"testRegex": "/tests/.*\\.(test|spec)\\.(ts)$",
|
|
||||||
"testURL": "http://localhost/",
|
|
||||||
"coverageThreshold": {
|
|
||||||
"global": {
|
|
||||||
"statements": 90,
|
|
||||||
"branches": 90,
|
|
||||||
"functions": 90,
|
|
||||||
"lines": 90
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"collectCoverageFrom": [
|
|
||||||
"src/**/*.ts",
|
|
||||||
"!**/node_modules/**",
|
|
||||||
"!**/vendor/**"
|
|
||||||
],
|
|
||||||
"coverageReporters": [
|
|
||||||
"lcov",
|
|
||||||
"text"
|
|
||||||
],
|
|
||||||
"verbose": true
|
|
||||||
}
|
|
||||||
14729
package-lock.json
generated
14729
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
55
package.json
55
package.json
@ -1,55 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "btcpay",
|
"name": "btcpay",
|
||||||
"version": "0.2.5",
|
"version": "0.1.0",
|
||||||
"description": "A nodejs client implementation for BTCPay",
|
"description": "A nodejs client implementation for BTCPay",
|
||||||
"main": "dist/index.js",
|
"main": "index.js",
|
||||||
"types": "dist/index.d.ts",
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rimraf dist && tsc -p ./tsconfig.json",
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
"coverage": "npm run unit -- --coverage",
|
|
||||||
"format": "npm run prettier -- --write",
|
|
||||||
"format:ci": "npm run prettier -- --check",
|
|
||||||
"lint": "tslint -p tsconfig.json -c tslint.json",
|
|
||||||
"prepare": "npm run build",
|
|
||||||
"prettier": "prettier src/**/*.ts tests/**/*.spec.ts --single-quote --trailing-comma all",
|
|
||||||
"test": "npm run format:ci && npm run lint && npm run coverage",
|
|
||||||
"unit": "jest --config=jest.json --runInBand"
|
|
||||||
},
|
},
|
||||||
"contributors": [
|
"author": "Tim Akinbo <tim@tanjalo.com>",
|
||||||
"Tim Akinbo <tim@tanjalo.com>",
|
|
||||||
"Christoph Ott <christoph.ott@lean-coders.at>",
|
|
||||||
"Jonathan Underwood <junderwood@bitcoinbank.co.jp>"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/btcpayserver/node-btcpay.git"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bluebird": "^3.5.1",
|
||||||
|
"bn.js": "^4.11.8",
|
||||||
"bs58": "^4.0.1",
|
"bs58": "^4.0.1",
|
||||||
"elliptic": "^6.5.4",
|
"elliptic": "^6.4.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.85.0",
|
||||||
"request-promise": "^4.2.6",
|
"request-promise": "^4.2.2",
|
||||||
"underscore": "^1.13.1"
|
"underscore": "^1.8.3"
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/bs58": "^4.0.1",
|
|
||||||
"@types/elliptic": "^6.4.12",
|
|
||||||
"@types/jest": "^26.0.23",
|
|
||||||
"@types/node": "^15.0.2",
|
|
||||||
"@types/puppeteer": "^5.4.3",
|
|
||||||
"@types/request": "^2.48.5",
|
|
||||||
"@types/request-promise": "^4.1.47",
|
|
||||||
"@types/underscore": "^1.11.2",
|
|
||||||
"jest": "^26.6.3",
|
|
||||||
"prettier": "^2.3.0",
|
|
||||||
"puppeteer": "^9.1.1",
|
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"ts-jest": "^26.5.6",
|
|
||||||
"tslint": "^5.20.1",
|
|
||||||
"typescript": "^4.2.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,153 +0,0 @@
|
|||||||
import * as elliptic from 'elliptic';
|
|
||||||
import * as qs from 'querystring';
|
|
||||||
import * as rp from 'request-promise';
|
|
||||||
import * as _ from 'underscore';
|
|
||||||
import {
|
|
||||||
CreateInvoiceArgs,
|
|
||||||
GetInvoicesArgs,
|
|
||||||
PairClientResponse,
|
|
||||||
} from '../models/client';
|
|
||||||
import { Cryptography as crypto } from './cryptography';
|
|
||||||
import { Invoice } from '../models/invoice';
|
|
||||||
import { Rate } from '../models/rate';
|
|
||||||
|
|
||||||
export class BTCPayClient {
|
|
||||||
private clientId: string;
|
|
||||||
private userAgent: string;
|
|
||||||
private options: any;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private host: string,
|
|
||||||
private kp: elliptic.ec.KeyPair,
|
|
||||||
private tokens: any = {},
|
|
||||||
) {
|
|
||||||
this.host = this.host.replace(/\/+$/, '');
|
|
||||||
this.clientId = crypto.get_sin_from_key(this.kp);
|
|
||||||
this.userAgent = 'node-btcpay';
|
|
||||||
this.options = {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Accept: 'application/json',
|
|
||||||
'User-Agent': this.userAgent,
|
|
||||||
'X-Accept-Version': '2.0.0',
|
|
||||||
},
|
|
||||||
json: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async pair_client(code: string): Promise<PairClientResponse> {
|
|
||||||
const re = new RegExp('^\\w{7}$');
|
|
||||||
|
|
||||||
if (!re.test(code)) {
|
|
||||||
throw new Error('pairing code is not valid');
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
id: this.clientId,
|
|
||||||
pairingCode: code,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.unsigned_request('/tokens', payload).then((data: any) => {
|
|
||||||
const _data = data[0];
|
|
||||||
const _res: any = {};
|
|
||||||
_res[_data.facade] = _data.token;
|
|
||||||
return _res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async get_rates(
|
|
||||||
currencyPairs: string[],
|
|
||||||
storeID: string,
|
|
||||||
): Promise<Rate[]> {
|
|
||||||
return this.signed_get_request('/rates', {
|
|
||||||
currencyPairs: currencyPairs.join(','),
|
|
||||||
storeID,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async create_invoice(
|
|
||||||
payload: CreateInvoiceArgs,
|
|
||||||
token?: any,
|
|
||||||
): Promise<Invoice> {
|
|
||||||
const re = new RegExp('^[A-Z]{3}$');
|
|
||||||
|
|
||||||
if (!re.test(payload.currency)) {
|
|
||||||
throw new Error('Currency is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNaN(parseFloat(payload.price as any))) {
|
|
||||||
throw new Error('Price must be a float');
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.signed_post_request('/invoices', payload, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async get_invoice(invoiceId: string, token?: any): Promise<Invoice> {
|
|
||||||
return this.signed_get_request('/invoices/' + invoiceId, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async get_invoices(
|
|
||||||
params?: GetInvoicesArgs,
|
|
||||||
token?: any,
|
|
||||||
): Promise<Invoice[]> {
|
|
||||||
return this.signed_get_request('/invoices', params, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
private create_signed_headers(uri: string, payload: string) {
|
|
||||||
return {
|
|
||||||
'X-Identity': Buffer.from(
|
|
||||||
this.kp.getPublic().encodeCompressed(),
|
|
||||||
).toString('hex'),
|
|
||||||
'X-Signature': crypto.sign(uri + payload, this.kp).toString('hex'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async signed_get_request(
|
|
||||||
path: string,
|
|
||||||
params: any = {},
|
|
||||||
token: any = _.values(this.tokens)[0],
|
|
||||||
): Promise<any> {
|
|
||||||
params.token = token;
|
|
||||||
|
|
||||||
const _options = JSON.parse(JSON.stringify(this.options));
|
|
||||||
|
|
||||||
const _uri = this.host + path;
|
|
||||||
const _payload = '?' + qs.stringify(params);
|
|
||||||
|
|
||||||
_.extend(_options.headers, this.create_signed_headers(_uri, _payload));
|
|
||||||
_options.uri = _uri;
|
|
||||||
_options.qs = params;
|
|
||||||
|
|
||||||
return rp.get(_options).then((resp: any) => resp.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async signed_post_request(
|
|
||||||
path: string,
|
|
||||||
payload: any,
|
|
||||||
token: any = _.values(this.tokens)[0],
|
|
||||||
): Promise<any> {
|
|
||||||
payload.token = token;
|
|
||||||
|
|
||||||
const _uri = this.host + path;
|
|
||||||
const _payload = JSON.stringify(payload);
|
|
||||||
const _options = JSON.parse(JSON.stringify(this.options));
|
|
||||||
|
|
||||||
_.extend(_options.headers, this.create_signed_headers(_uri, _payload));
|
|
||||||
_options.uri = _uri;
|
|
||||||
_options.body = payload;
|
|
||||||
|
|
||||||
return rp.post(_options).then((resp: any) => resp.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async unsigned_request(path: string, payload: any): Promise<any> {
|
|
||||||
const _mixin: any = {
|
|
||||||
method: 'POST',
|
|
||||||
uri: this.host + path,
|
|
||||||
body: payload,
|
|
||||||
};
|
|
||||||
|
|
||||||
const _options = { ...JSON.parse(JSON.stringify(this.options)), ..._mixin };
|
|
||||||
|
|
||||||
return rp(_options).then((resp: any) => resp.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
import * as elliptic from 'elliptic';
|
|
||||||
import * as bs58 from 'bs58';
|
|
||||||
import * as crypto from 'crypto';
|
|
||||||
|
|
||||||
const ec = new elliptic.ec('secp256k1');
|
|
||||||
|
|
||||||
export class Cryptography {
|
|
||||||
public static generate_keypair(): elliptic.ec.KeyPair {
|
|
||||||
const kp = ec.genKeyPair();
|
|
||||||
return kp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static load_keypair(
|
|
||||||
buf: Buffer | string | elliptic.ec.KeyPair,
|
|
||||||
): elliptic.ec.KeyPair {
|
|
||||||
return ec.keyFromPrivate(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static get_sin_from_key(kp: elliptic.ec.KeyPair): string {
|
|
||||||
const pk: crypto.BinaryLike = Buffer.from(
|
|
||||||
kp.getPublic().encodeCompressed(),
|
|
||||||
);
|
|
||||||
const version: Buffer = Cryptography.get_version_from_compressed_key(pk);
|
|
||||||
const checksum: Buffer = Cryptography.get_checksum_from_version(version);
|
|
||||||
return bs58.encode(Buffer.concat([version, checksum]));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static sign(
|
|
||||||
message: crypto.BinaryLike,
|
|
||||||
kp: elliptic.ec.KeyPair,
|
|
||||||
): Buffer {
|
|
||||||
const digest = crypto.createHash('sha256').update(message).digest();
|
|
||||||
return Buffer.from(kp.sign(digest).toDER());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static get_version_from_compressed_key(
|
|
||||||
pk: crypto.BinaryLike,
|
|
||||||
): Buffer {
|
|
||||||
const sh2 = crypto.createHash('sha256').update(pk).digest();
|
|
||||||
const rp = crypto.createHash('ripemd160').update(sh2).digest();
|
|
||||||
|
|
||||||
return Buffer.concat([
|
|
||||||
Buffer.from('0F', 'hex'),
|
|
||||||
Buffer.from('02', 'hex'),
|
|
||||||
rp,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static get_checksum_from_version(version: crypto.BinaryLike): Buffer {
|
|
||||||
const h1 = crypto.createHash('sha256').update(version).digest();
|
|
||||||
const h2 = crypto.createHash('sha256').update(h1).digest();
|
|
||||||
|
|
||||||
return h2.slice(0, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { Cryptography } from './core/cryptography';
|
|
||||||
import { BTCPayClient } from './core/client';
|
|
||||||
|
|
||||||
export { Invoice } from './models/invoice';
|
|
||||||
export { Rate } from './models/rate';
|
|
||||||
export { Cryptography as crypto, BTCPayClient };
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
export interface PairClientResponse {
|
|
||||||
merchant: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetInvoicesArgs {
|
|
||||||
status?: string;
|
|
||||||
orderId?: string;
|
|
||||||
itemCode?: string;
|
|
||||||
dateStart?: string;
|
|
||||||
dateEnd?: string;
|
|
||||||
limit?: number;
|
|
||||||
offset?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateInvoiceArgs {
|
|
||||||
currency: string;
|
|
||||||
price: number;
|
|
||||||
orderId?: string | number;
|
|
||||||
expirationTime?: string;
|
|
||||||
itemDesc?: string;
|
|
||||||
itemCode?: string;
|
|
||||||
posData?: string;
|
|
||||||
status?: string;
|
|
||||||
redirectUrl?: string;
|
|
||||||
transactionSpeed?: 'low' | 'low-medium' | 'medium' | 'high';
|
|
||||||
physical?: boolean;
|
|
||||||
supportedTransactionCurrencies?: {
|
|
||||||
[index: string]: {
|
|
||||||
enabled: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
refundable?: boolean;
|
|
||||||
taxIncluded?: number;
|
|
||||||
token?: string;
|
|
||||||
redirectAutomatically?: boolean;
|
|
||||||
notificationEmail?: string;
|
|
||||||
notificationURL?: string;
|
|
||||||
extendedNotifications?: boolean;
|
|
||||||
fullNotifications?: boolean;
|
|
||||||
buyerEmail?: string;
|
|
||||||
buyerPhone?: string;
|
|
||||||
buyerCountry?: string;
|
|
||||||
buyerZip?: string;
|
|
||||||
buyerState?: string;
|
|
||||||
buyerCity?: string;
|
|
||||||
buyerAddress2?: string;
|
|
||||||
buyerAddress1?: string;
|
|
||||||
buyerName?: string;
|
|
||||||
}
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
export interface Invoice {
|
|
||||||
url: string;
|
|
||||||
posData: string | null;
|
|
||||||
status: 'new' | 'paid' | 'confirmed' | 'complete' | 'expired' | 'invalid';
|
|
||||||
btcPrice: string;
|
|
||||||
btcDue: string;
|
|
||||||
cryptoInfo: Array<{
|
|
||||||
cryptoCode: string;
|
|
||||||
paymentType: string;
|
|
||||||
rate: number;
|
|
||||||
exRates: any[];
|
|
||||||
paid: string;
|
|
||||||
price: string;
|
|
||||||
due: string;
|
|
||||||
paymentUrls: any[];
|
|
||||||
address: string;
|
|
||||||
url: string;
|
|
||||||
totalDue: string;
|
|
||||||
networkFee: string;
|
|
||||||
txCount: number;
|
|
||||||
cryptoPaid: string;
|
|
||||||
payments: any[];
|
|
||||||
}>;
|
|
||||||
price: number;
|
|
||||||
currency: string;
|
|
||||||
exRates: any;
|
|
||||||
buyerTotalBtcAmount: string | null;
|
|
||||||
itemDesc: string | null;
|
|
||||||
itemCode: string | null;
|
|
||||||
orderId: string | null;
|
|
||||||
guid: string;
|
|
||||||
id: string;
|
|
||||||
invoiceTime: number;
|
|
||||||
expirationTime: number;
|
|
||||||
currentTime: number;
|
|
||||||
lowFeeDetected: boolean;
|
|
||||||
btcPaid: string;
|
|
||||||
rate: number;
|
|
||||||
exceptionStatus: false | 'paidOver' | 'paidLate' | 'paidPartial' | 'marked';
|
|
||||||
paymentUrls: {
|
|
||||||
BIP21: string | null;
|
|
||||||
BIP72: string | null;
|
|
||||||
BIP72b: string | null;
|
|
||||||
BIP73: string | null;
|
|
||||||
BOLT11: string | null;
|
|
||||||
};
|
|
||||||
refundAddressRequestPending: boolean;
|
|
||||||
buyerPaidBtcMinerFee: string | null;
|
|
||||||
bitcoinAddress: string;
|
|
||||||
token: string;
|
|
||||||
flags: {
|
|
||||||
refundable: boolean;
|
|
||||||
};
|
|
||||||
paymentSubtotals: any;
|
|
||||||
paymentTotals: any;
|
|
||||||
amountPaid: number;
|
|
||||||
minerFees: any;
|
|
||||||
exchangeRates: any;
|
|
||||||
supportedTransactionCurrencies: any;
|
|
||||||
addresses: any;
|
|
||||||
paymentCodes: any;
|
|
||||||
buyer: {
|
|
||||||
name: string | null;
|
|
||||||
address1: string | null;
|
|
||||||
address2: string | null;
|
|
||||||
locality: string | null;
|
|
||||||
region: string | null;
|
|
||||||
postalCode: string | null;
|
|
||||||
country: string | null;
|
|
||||||
phone: string | null;
|
|
||||||
email: string | null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
export interface Rate {
|
|
||||||
name: string;
|
|
||||||
cryptoCode: string;
|
|
||||||
currencyPair: string;
|
|
||||||
code: string;
|
|
||||||
rate: number;
|
|
||||||
}
|
|
||||||
@ -1,182 +0,0 @@
|
|||||||
import * as elliptic from 'elliptic';
|
|
||||||
import * as puppeteer from 'puppeteer';
|
|
||||||
import { Browser, Page } from 'puppeteer';
|
|
||||||
import { BTCPayClient } from '../../src/core/client';
|
|
||||||
import { Cryptography as myCrypto } from '../../src/core/cryptography';
|
|
||||||
|
|
||||||
const IGNORE_SANDBOX_ERROR = process.env['BTCPAY_IGNORE_SANDBOX_ERROR'];
|
|
||||||
const USER_NAME = 'test@example.com';
|
|
||||||
const PASSWORD = 'satoshinakamoto';
|
|
||||||
const URL = 'http://127.0.0.1:49392';
|
|
||||||
|
|
||||||
const MY_PRIVATE_KEY = Buffer.from(
|
|
||||||
'31eb31ecf1a640c9d1e0a1105501f36235f8c7d51d67dcf74ccc968d74cb6b25',
|
|
||||||
'hex',
|
|
||||||
);
|
|
||||||
let STORE_ID = '';
|
|
||||||
const WINDOW_WIDTH = 1920;
|
|
||||||
const WINDOW_HEIGHT = 1080;
|
|
||||||
|
|
||||||
let INVOICE_ID = '';
|
|
||||||
const HEADLESS = true;
|
|
||||||
|
|
||||||
const loginAndGetPairingCode = async (): Promise<{
|
|
||||||
browser: Browser;
|
|
||||||
page: Page;
|
|
||||||
pairingCode: string;
|
|
||||||
}> => {
|
|
||||||
const newTokenName = 'autotest ' + new Date().getTime();
|
|
||||||
|
|
||||||
const browser = await puppeteer
|
|
||||||
.launch({
|
|
||||||
headless: HEADLESS,
|
|
||||||
args: ['--window-size=' + WINDOW_WIDTH + ',' + WINDOW_HEIGHT],
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
(v) => v, // if success, passthrough
|
|
||||||
// if error, check for env and ignore sandbox and warn.
|
|
||||||
(err) => {
|
|
||||||
if (IGNORE_SANDBOX_ERROR === '1') {
|
|
||||||
console.warn(
|
|
||||||
'WARNING!!! Error occurred, Chromium will be started ' +
|
|
||||||
"without sandbox. This won't guarantee success.",
|
|
||||||
);
|
|
||||||
return puppeteer.launch({
|
|
||||||
headless: HEADLESS,
|
|
||||||
ignoreDefaultArgs: ['--disable-extensions'],
|
|
||||||
args: [
|
|
||||||
'--no-sandbox',
|
|
||||||
'--disable-setuid-sandbox',
|
|
||||||
'--window-size=' + WINDOW_WIDTH + ',' + WINDOW_HEIGHT,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.warn(
|
|
||||||
'If "No usable sandbox!" error, retry test with ' +
|
|
||||||
'BTCPAY_IGNORE_SANDBOX_ERROR=1',
|
|
||||||
);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const page = (await browser.pages())[0];
|
|
||||||
await page.setViewport({ width: WINDOW_WIDTH, height: WINDOW_HEIGHT });
|
|
||||||
try {
|
|
||||||
await page.goto(URL + '/Account/Login');
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message === `net::ERR_CONNECTION_REFUSED at ${URL}/Account/Login`) {
|
|
||||||
browser.close();
|
|
||||||
console.log(
|
|
||||||
'Please start docker container locally:\n' +
|
|
||||||
'docker run -p 127.0.0.1:49392:49392 junderw/btcpay-client-test-server',
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
page,
|
|
||||||
browser,
|
|
||||||
pairingCode: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
await page.click('#LoginButton');
|
|
||||||
await page.goto(URL + '/stores');
|
|
||||||
await page.waitForSelector('#CreateStore');
|
|
||||||
await page.click(
|
|
||||||
'table.table.table-sm.table-responsive-md > tbody > ' +
|
|
||||||
'tr:nth-of-type(1) > td:nth-of-type(3) > a:nth-of-type(2)',
|
|
||||||
);
|
|
||||||
await page.waitForSelector('#Id');
|
|
||||||
const idElement = await page.$$('#Id');
|
|
||||||
STORE_ID = (await idElement[0]
|
|
||||||
.getProperty('value')
|
|
||||||
.then((v) => v?.jsonValue())) as string;
|
|
||||||
await page.goto(URL + '/stores/' + STORE_ID + '/Tokens/Create');
|
|
||||||
await page.waitForSelector('input#Label');
|
|
||||||
await page.waitForSelector('[type="submit"]');
|
|
||||||
|
|
||||||
await page.type('#Label', newTokenName);
|
|
||||||
await page.click('[type="submit"]');
|
|
||||||
await page.waitForSelector('button[type="submit"]');
|
|
||||||
await page.click('[type="submit"]');
|
|
||||||
await page.waitForSelector('div.alert.alert-success.alert-dismissible');
|
|
||||||
const contents = await page.evaluate(() => {
|
|
||||||
const el = document.querySelector(
|
|
||||||
'div.alert.alert-success.alert-dismissible',
|
|
||||||
);
|
|
||||||
if (el === null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return el.innerHTML;
|
|
||||||
});
|
|
||||||
const pairingCode = (contents.match(
|
|
||||||
/Server initiated pairing code: (\S{7})/,
|
|
||||||
) || [])[1];
|
|
||||||
if (!pairingCode) {
|
|
||||||
throw new Error('Could not get pairing code');
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
browser,
|
|
||||||
page,
|
|
||||||
pairingCode,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
let MY_KEYPAIR: elliptic.ec.KeyPair;
|
|
||||||
let client: BTCPayClient;
|
|
||||||
describe('btcpay.core.client', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
jest.setTimeout(20000); // browser takes a while
|
|
||||||
MY_KEYPAIR = myCrypto.load_keypair(MY_PRIVATE_KEY);
|
|
||||||
client = new BTCPayClient(URL, MY_KEYPAIR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should pair with server', async () => {
|
|
||||||
const pairingData = await loginAndGetPairingCode();
|
|
||||||
const myClient = new BTCPayClient(URL, MY_KEYPAIR);
|
|
||||||
const result = await myClient.pair_client(pairingData.pairingCode);
|
|
||||||
client = new BTCPayClient(URL, MY_KEYPAIR, result);
|
|
||||||
expect(result.merchant).toBeDefined();
|
|
||||||
pairingData.browser.close();
|
|
||||||
await expect(myClient.pair_client('hduheufhfuf')).rejects.toThrow(
|
|
||||||
/^pairing code is not valid$/,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get rates', async () => {
|
|
||||||
const results = await client.get_rates(['LTC_USD', 'BTC_USD'], STORE_ID);
|
|
||||||
expect(results[0].rate).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create an invoice', async () => {
|
|
||||||
const results = await client.create_invoice({
|
|
||||||
currency: 'USD',
|
|
||||||
price: 1.12,
|
|
||||||
});
|
|
||||||
INVOICE_ID = results.id;
|
|
||||||
expect(results.bitcoinAddress).toBeDefined();
|
|
||||||
await expect(
|
|
||||||
client.create_invoice({
|
|
||||||
currency: 'KDFAHKJFKJ',
|
|
||||||
price: 1.12,
|
|
||||||
}),
|
|
||||||
).rejects.toThrow(/^Currency is invalid$/);
|
|
||||||
await expect(
|
|
||||||
client.create_invoice({
|
|
||||||
currency: 'USD',
|
|
||||||
// @ts-ignore
|
|
||||||
price: 'xkhdfhu',
|
|
||||||
}),
|
|
||||||
).rejects.toThrow(/^Price must be a float$/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get invoice', async () => {
|
|
||||||
const results = await client.get_invoice(INVOICE_ID);
|
|
||||||
expect(results.id).toBe(INVOICE_ID);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get multiple invoices', async () => {
|
|
||||||
const results = await client.get_invoices();
|
|
||||||
expect(results[0].bitcoinAddress).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
import { Cryptography as myCrypto } from '../../src/core/cryptography';
|
|
||||||
|
|
||||||
const MY_PRIVATE_KEY = Buffer.from(
|
|
||||||
'31eb31ecf1a640cd91e0a1105501f36235f8c7d51d67dcf74ccc968d74cb6b25',
|
|
||||||
'hex',
|
|
||||||
);
|
|
||||||
|
|
||||||
describe('btcpay.core.cryptography', () => {
|
|
||||||
it('should generate a keypair', () => {
|
|
||||||
const kp = myCrypto.generate_keypair();
|
|
||||||
const priv = kp.getPrivate();
|
|
||||||
expect(priv).toBeDefined();
|
|
||||||
});
|
|
||||||
it('should load a keypair from Buffer', () => {
|
|
||||||
const kp = myCrypto.load_keypair(MY_PRIVATE_KEY);
|
|
||||||
const priv = kp.getPrivate();
|
|
||||||
expect(priv).toBeDefined();
|
|
||||||
});
|
|
||||||
it('should get sin from key', () => {
|
|
||||||
const kp = myCrypto.load_keypair(MY_PRIVATE_KEY);
|
|
||||||
const sin = myCrypto.get_sin_from_key(kp);
|
|
||||||
expect(sin).toBe('TfDnXWvj6bBhkduYiZnohg5qhtDu5VWohhw');
|
|
||||||
});
|
|
||||||
it('should sign a message', () => {
|
|
||||||
const kp = myCrypto.load_keypair(MY_PRIVATE_KEY);
|
|
||||||
const message = Buffer.from('Satoshi', 'utf8');
|
|
||||||
const sig = myCrypto.sign(message, kp);
|
|
||||||
expect(sig.toString('hex')).toBe(
|
|
||||||
'304402205b0a505c180bddbd4a8836de0f2ac10b52b327d0e932352d28d170fb81517a' +
|
|
||||||
'770220307ac2ec2134d81fd04df6a1662b0962ad1322209c2e45ff8af63d3f12e0d089',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import * as btcpay from '../src/index';
|
|
||||||
|
|
||||||
describe('btcpay.index', () => {
|
|
||||||
it('should import', () => {
|
|
||||||
expect(btcpay).toBeDefined();
|
|
||||||
expect(btcpay.crypto).toBeDefined();
|
|
||||||
expect(btcpay.BTCPayClient).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es2017",
|
|
||||||
"module": "commonjs",
|
|
||||||
"strict": true,
|
|
||||||
"declaration": true,
|
|
||||||
"outDir": "dist",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"esModuleInterop": false
|
|
||||||
},
|
|
||||||
"lib": [
|
|
||||||
"es2017",
|
|
||||||
"node"
|
|
||||||
],
|
|
||||||
"include": [
|
|
||||||
"src/**/*.ts"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"src/**/*.spec.ts",
|
|
||||||
"node_modules",
|
|
||||||
"tests"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
80
tslint.json
80
tslint.json
@ -1,80 +0,0 @@
|
|||||||
{
|
|
||||||
"rules": {
|
|
||||||
"align": [false, "parameters", "arguments", "statements"],
|
|
||||||
"ban": [true, ["angular", "forEach"]],
|
|
||||||
"class-name": true,
|
|
||||||
"comment-format": [false, "check-space", "check-lowercase"],
|
|
||||||
"curly": true,
|
|
||||||
"eofline": false,
|
|
||||||
"forin": true,
|
|
||||||
"indent": [true, 4],
|
|
||||||
"interface-name": false,
|
|
||||||
"jsdoc-format": true,
|
|
||||||
"label-position": true,
|
|
||||||
"max-line-length": [false, 140],
|
|
||||||
"member-ordering": [
|
|
||||||
false,
|
|
||||||
"public-before-private",
|
|
||||||
"static-before-instance",
|
|
||||||
"variables-before-functions"
|
|
||||||
],
|
|
||||||
"no-any": false,
|
|
||||||
"no-arg": true,
|
|
||||||
"no-bitwise": true,
|
|
||||||
"no-console": [false, "debug", "info", "time", "timeEnd", "trace"],
|
|
||||||
"no-construct": true,
|
|
||||||
"no-constructor-vars": false,
|
|
||||||
"no-debugger": false,
|
|
||||||
"no-shadowed-variable": true,
|
|
||||||
"no-duplicate-variable": true,
|
|
||||||
"no-empty": false,
|
|
||||||
"no-eval": true,
|
|
||||||
"no-require-imports": true,
|
|
||||||
"no-string-literal": false,
|
|
||||||
"no-switch-case-fall-through": true,
|
|
||||||
"no-trailing-whitespace": true,
|
|
||||||
"no-unused-expression": true,
|
|
||||||
"no-use-before-declare": true,
|
|
||||||
"no-var-keyword": true,
|
|
||||||
"no-var-requires": true,
|
|
||||||
"one-line": [
|
|
||||||
true,
|
|
||||||
"check-catch",
|
|
||||||
"check-else",
|
|
||||||
"check-open-brace",
|
|
||||||
"check-whitespace"
|
|
||||||
],
|
|
||||||
"quotemark": [true, "single"],
|
|
||||||
"radix": false,
|
|
||||||
"semicolon": true,
|
|
||||||
"triple-equals": [true, "allow-null-check"],
|
|
||||||
"typedef": [
|
|
||||||
true,
|
|
||||||
"callSignature",
|
|
||||||
"catchClause",
|
|
||||||
"indexSignature",
|
|
||||||
"parameter",
|
|
||||||
"propertySignature",
|
|
||||||
"variableDeclarator"
|
|
||||||
],
|
|
||||||
"typedef-whitespace": [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
"call-signature": "nospace",
|
|
||||||
"index-signature": "nospace",
|
|
||||||
"parameter": "nospace",
|
|
||||||
"property-declaration": "nospace",
|
|
||||||
"variable-declaration": "nospace"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"variable-name": [true, "allow-leading-underscore"],
|
|
||||||
"whitespace": [
|
|
||||||
true,
|
|
||||||
"check-branch",
|
|
||||||
"check-decl",
|
|
||||||
"check-operator",
|
|
||||||
"check-separator",
|
|
||||||
"check-type"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user