- As a user, I want to have ability to topup my balance with Bitcoin and send payments within Lightning network.
- As a product owner, I want to have transparent usage statistics and run-time information on payment channels and environment.
-
LndHub API is standalone software and needs LND client synchronized and running. LndHub API is not a Lightning wallet in terms of funds storage, it operates whole amount of available funds on channels. User's balances and transactions stored in internal database.
-
LndHub API is accessible for everyone, but only
/create
can be called without authorization token. -
To start sending lightning payments user should top-up his Bitcoin balance, by sending Bitcoins to address assigned to corresponding user id. User should wait for 3 confirmations after which funds will be available for Lightning payments.
-
gRPC RPC framework is used for communication with LND. See https://github.com/lightningnetwork/lnd/tree/master/lnrpc
-
Outh2 library, MongoDB and Golang backend is used for API implementation. Every request from user is signed and associated with corresponding user id.
-
Double entry system is used for internal accounting https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system 6.1. Internal accounting requirements https://github.com/matveyco/lnd-wallet-api-spec/edit/master/Accounting-requirements.md
-
All amounts are satoshis (int), although millisatoshis are used in LND internally (rounding is up to server implementation).
-
Every account has its separate Lightning, BTC addresses and unique session. If user runs few accounts from one device or wallet, corresponding amount of sessions should be opened.
-
All json keys should be in snake_case
Call | Method | Handler | Params | Return | Description |
---|---|---|---|---|---|
Create Account | POST | /create | {none} | JSON Auth Data | Create new user account and get credentials |
Authorize | POST | /auth | auth params (login/password or refresh_token) | JSON token data | Authorize user with Oauth. When user use refresh_token to auth, then this refresh_token not available for access once again. Use new refresh_token |
Get token | POST | /oauth2/token | user id, secret, grant_type and scope | token data | Get token data from user id, secret, grant_type and scope |
Get BTC Addr | GET | /getbtc | {none} | Text address | Get user's BTC address to top-up his account |
New BTC Addr | POST | /newbtc | {none} | Text address | Create new BTC address for user. Old addresses should remain valid, so if user accidentaly sends money to old address transaction will be assigned to his account |
Get Pending Balance | GET | /getpending | {none} | JSON | Get information about BTC pending transactions which have less than 3 confirmations |
Decode Invoice | GET | /decodeinvoice | Invoice string | JSON | Decode invoice from invoice string. If invoice is represented as QR-code, fronted device should decode it first |
Check Route | GET | /checkroute | Payment destination | Success | Check if payment destination is available and invoice could be paid |
Pay invoice | POST | /payinvoice | Invoice string | Success | Pay invoice. Before payment invoice should be read and destination checked, also balance sum should be enough |
Send coins | POST | /sendcoins | Payment destination | Success | Just send coins to a specified location (Lightning address) |
Get transactions | GET | /gettxs | Offset, limit | JSON array | Get transactions for a wallet. With load offset at limit |
Get transaction | GET | /gettx | Tx id | JSON | Get tx info by its ID |
Get balance | GET | /balance | {none} | int64 | Available unspent internal balance (in Satoshis) |
Get info | GET | /getinfo | {none} | JSON | Tech info. Fee on transactions for current user (0 for a start), available actual funds on channel, maximum tx size, service status etc. |
Get info | POST | /addinvoice | JSON | JSON | Create invoice. |
Get info | GET | /getuserinvoices | {none} | JSON | List of invoices created by user. |
GET requests pass data as GET params (GET /hello?foo=bar&aaa=bbb
).
POST requests pass data as contentType: "application/json; charset=utf-8"
.
Response is always JSON.
ok:true
should be always present.
{
"ok": true // boolean
}
error:true
should be always present.
{
"error" : true, // boolean
"code" : 1, // int
"message": "..." // string
}
Error code | Error message |
---|---|
1 | Bad auth |
2 | Not enough balance |
3 | Bad partner |
4 | Not a valid invoice |
5 | Lnd route not found |
6 | General server error |
7 | LND failure |
Create new user account and get credentials. Not whitelisted partners should return error.
при создании аккаунта можно добавить accouttype "accounttype": "test"
Request:
{
"partnerid" : "bluewallet" // string, not mandatory parameter
"accounttype" : "..." // string, not mandatory, default is common, also can be test or core
}
Response:
{
"login":"...", // string
"password":"...", // string
}
Authorize user with Oauth user and login
Request:
{
"login": "...", // string
"password": "..." // string
}
Response:
{
"access_token": "...", // string
"token_type": "...", // string
"refresh_token": "...", // string
"expiry": "0001-01-01T00:00:00Z" // datetime
}
Authorize user with Oauth user and login
Request:
{
"refresh_token": "...", // string
}
Response:
{
"access_token": "...", // string
"token_type": "...", // string
"refresh_token": "...", // string
"expiry": "0001-01-01T00:00:00Z" // datetime
}
Authorize user with Oauth user and login
Request:
{
"grant_type": "client_credentials", // string
"client_id": "...", // string
"client_secret": "..." // string
}
Response:
{
"access_token": "...", // string
"token_type": "...", // string
"refresh_token": "...", // string
"expiry": "0001-01-01T00:00:00Z" // datetime
}
Get user's BTC address to top-up his account
Request:
none
Response: [ { address: "..." // string }, ]
Create new BTC address for user. Old addresses should remain valid, so if user accidentaly sends money to old address transaction will be assigned to his account
Request:
none
Response:
{
address: "..." // string
}
Get information about BTC pending transactions which have less than 3 confirmations or lnd not settled invoice payments
Request:
none
Response:
{
[ // array of Transaction object (see below)
{
...
}
]
}
Decode invoice from invoice string. If invoice is represented as QR-code, fronted device should decode it first
Request:
{
"invoice" : "..." // string with bolt11 invoice
}
Response:
{
"destination": "...", // string, lnd node address
"payment_hash": "...", // string
"num_satoshis": "78497", // string, satoshis
"timestamp": "1534430501", // string, unixtime
"expiry": "3600", // string, seconds
"description": "...", // string
"description_hash": "", // string
"fallback_addr": "...", // string, fallback on-chain address
"cltv_expiry": "...", // string, delta to use for the time-lock of the CLTV extended to the final hop
"route_hints": [
{
"hop_hints" : [
{
"node_id": "..", // string, the public key of the node at the start of the
// channel.
"chan_id": ..., // int, the unique identifier of the channel.
"fee_base_msat": ..., // int, The base fee of the channel denominated in
// millisatoshis.
"fee_proportional_millionths": ...,
// int, the fee rate of the channel
// for sending one satoshi across it denominated
// in millionths of a satoshi
"cltv_expiry_delta": ...
// int, the fee rate of the channel for sending one satoshi
// across it denominated in millionths of a satoshi
}, ...
]
}, ...
]
}
Check if payment destination is available and invoice could be paid
Request:
{
"invoice" : "..." // string with bolt11 invoice
}
Response:
{
"ok" : true // boolean
}
Check if payment destination is available and invoice could be paid
Request:
{
"destination" : "..." // string, destination lnd node address
"amt": "..." // string,
}
Response:
{
"ok" : true // boolean
}
Pay invoice. Before payment invoice should be read and destination checked, also balance sum should be enough
Request:
{
"invoice" : "..." // string with bolt11 invoice
}
Response:
{
"payment_error": "..." // string
"payment_preimage": "..." // string
"payment_route": {
"total_time_lock": ... , // int
"total_fees": ... , // int
"total_amt": ... , // int
"total_fees_msat": ... , // int
"total_amt_msat": ... , // int
"hops": [
{
"chan_id": ... , // int
"chan_capacity": ... , // int
"amt_to_forward": ... , // int
"fee": ... , // int
"expiry": ... , // int
"amt_to_forward_msat": ... , // int
"fee_msat": ... , // int
},
]
}
}
Just send coins to a specified location (Lightning address)
Request:
{
"invoice" : "..." // string with bolt11 invoice
}
Response:
{
... // Transaction object (see below)
}
Get successful lightning and btc transactions user made. Order newest to oldest.
Request:
{
"limit" : 10, // INT
"offset": 0, // INT
}
Response:
{
[ // array of Transaction object (see below)
{
...
}
]
}
Get info on successful lightning transaction user made. TXID is an internal LndHub identifier, no relation to onchain bitcoin txid.
Request:
{
"txid" : 666 // INT
}
Response: { ... // Transaction object (see below) }
Returns balance user can spend on lightning payments.
Request:
none
Response:
{
"BTC": { // string, currency
"TotalBalance": 109388, // int, satoshis
"AvailableBalance": 109388, // int, satoshis
"UncomfirmedBalance": 0 // int, satoshis
}, ...
//now available only btc balance
}
Returns fees user pays for payments, status of the system, etc.
Request:
none
Response:
{
"fee": 0, // int, in cents of percent, i.e. 100 for 1%, 50 for 0.5%, 1 for 0.01%
"identity_pubkey": "...", // string, lnd node identity pubkey
"alias": "...", // string, lnd node alias
"num_pending_channels": 0, // int
"num_active_channels": 3, // int
"num_peers": 6, // int
"block_height": 542389, // int
"block_hash": "...", // string
"synced_to_chain": true, // bool
"testnet": false,
"chains": [
"bitcoin" // string, available chans to operate by lnd
],
"uris": [
"...", // string, uris of lnd node
],
"best_header_timestamp": "...", // string, unixtime
"version": "..." // string, lnd version
}
Returns fees user pays for payments, status of the system, etc.
Request:
{
"amt": "...", // string
"memo":"...", // string
"receipt":"...", // string, not mandatory parameter
"preimage": "...", // string, not mandatory parameter
"fallbackAddr": "...", // string, not mandatory parameter
"expiry": "...", // string, not mandatory parameter
"private": "..." // string, not mandatory parameter
}
Response:
{
"r_hash": "...", // string,
"pay_req": "...", // string, a bare-bones invoice for a payment within the Lightning Network
"add_index": ... // int, The “add” index of this invoice. Each newly created invoice will
// increment this index making it monotonically increasing.
// Callers to the SubscribeInvoices call can use this to instantly
// get notified of all added invoices with an add_index greater than this one.
}
Returns fees user pays for payments, status of the system, etc.
Request:
none
Response:
{
"r_hash": "...", // string
"payment_request": "...", // string
"add_index": "...", // string
"description": "...", // string
"amt": ... , // int
"ispaid": ... // bool
}
{
"type": "...", // string, type of txs. Types:
// bitcoind_internal_tx - moves to user btc address or account
// bitcoind_tx - received by address or account
// paid_invoice - user paid someone's invoice
// sent_coins - user sent coins by lnd to someone's btc account
// received_invoice_payments - user received payments by invoice
"txid": "...", // string, internal tx id. not related to onchain transaction id
"amt": 666, // satoshi, int
"fee": 11, // satoshi, int
"timestamp": 1234567, // int, unixtime
"from": "...", // string
"to": "...", // string
"description": "...", // string, user-defined text
"invoice": "...", // string, original bolt11-format invoice
}
Oauth2 process consists of such stages as:
- Client (someone, who use api), make request to Authorization service with credentials (POST /auth?type=auth)
- Authorization service checks credentials and searches for appropriate user id and secret (stored on Authorization service and Token service) and sends user id and secret to Token service (for example POST /getinfo/oauth2/token)
- Token service checks user id and secret and sends token data with refresh token to Authorization service which sends it to Client
- Client uses token to access protected resources (GET ?access_token=XXXXXXXXXXXXXX)
- When token expires or needs to refresh token for security issues Client sends refresh_token to Token service (POST /auth?type=refresh_token), which sends new token data with refresh_token and disables to access old