Baron is a Bitcoin payment processor that anyone can deploy
- Allow creation of invoices denominated in USD or BTC from any other application with the API key.
- Invoices denominated in USD are quoted at market-rate in BTC at the time of payment.
- Records BTC exchange rates when payments are made, useful for accounting.
- Keeps history of all invoices and payments.
- Keeps history of unusual events like reorg, double-spend, etc.
- Properly handles incoming payments after recovering from downtime.
- Notifies external applications via webhooks when an invoice is paid or rendered invalid.
- No lost notifications: monitors for success/failure of webhooks with a retry queue.
Clone the repository:
$ git clone https://github.com/slickage/baron.git
Change directories to Baron and install dependencies:
$ npm install
NOTE: Before proceeding with Baron's installation ensure that couchdb is using the 'random' algorithm instead of the default 'sequential'. This is to prevent easy guessing of invoice id hashes.
Configurations can be changed in the config.js file in the root of Baron.
var config = {
couchdb: {
url: process.env.DB_URL || 'http://localhost:5984',
name: process.env.DB_NAME || 'baron'
},
bitcoind: {
host: process.env.BITCOIND_HOST || 'localhost',
port: process.env.BITCOIND_PORT || 18332,
user: process.env.BITCOIND_USER || 'username',
pass: process.env.BITCOIND_PASS || 'password'
},
port: process.env.PORT || 8080,
baronAPIKey: process.env.BARON_API_KEY || 'youshouldreallychangethis',
chainExplorerUrl: process.env.CHAIN_EXPLORER_URL || 'http://tbtc.blockr.io/tx/info',
updateWatchListInterval: process.env.UPDATE_WATCH_LIST_INTERVAL || 15000,
lastBlockJobInterval: process.env.LAST_BLOCK_JOB_INTERVAL || 15000,
webhooksJobInterval: process.env.WEBHOOKS_JOB_INTERVAL || 15000,
paymentValidForMinutes: process.env.PAYMENT_VALID_FOR_MINUTES || 5,
trackPaymentUntilConf: process.env.TRACK_PAYMENT_UNTIL_CONF || 100
};
couchdb
- Database connection configsbitcoind
- Bitcoin client connetion configsport
- The port that Baron should run onbaronAPIKey
- A secret key that is used to validate invoice creation [1]chainExplorerUrl
- A link to the tx route of a chain explorerupdateWatchListInterval
- How often the watched payments job should run in mslastBlockJobInterval
- How often the last block job should run in mswebhooksJobInterval
- How often the webhooks job should run in mspaymentValidForMinutes
- How long before exchange rate refreshes for paymenttrackPaymentUntilConf
- How long to watch payments for before no longer updating
NOTES:
- [1] The
baronAPIKey
can be generated usingnode generatetoken.js stringToHash
. - Properties in config.js can be overriden with environment variables. Common ways to do this is with a .env file and foreman or an EnvironmentFile with systemd.
Modify bitcoin's bitcoin.conf:
# (optional) connects bitcoin client to testnet
testnet=1
# allows json-rpc api calls from Baron
server=1
# these should match your config or .env bitcoind username and password
rpcuser=username
rpcpassword=password
# tells bitcoind to post wallet/block notifications to baron
# the addresses below should match baron's address and port.
walletnotify=curl -o /dev/null -s -H "Content-Type: application/json" --data "{ \"txid\": \"%s\", \"api_key\": \"youshouldreallychangethis\" }" http://localhost:8080/walletnotify
blocknotify=curl -o /dev/null -s -H "Content-Type: application/json" --data "{ \"blockhash\": \"%s\", \"api_key\": \"youshouldreallychangethis\" }" http://localhost:8080/blocknotify
Note: Be sure to customize the two instances of api_key
within bitcoin.conf to match the BARON_API_KEY
configuration of Baron. Additionally the /walletnotify
and /bocknotify
URL's must be correct to the hostname and port of your Baron instance and network accessible from the bitcoind. Please be certain to protect the network between bitcoind and Baron by running it on localhost, within a private network, or VPN.
Both bitcoind and CouchDB must be running and Baron must be correctly configured to reach these external services.
Running Baron with node
$ node server.js
Running Baron with foreman and nodemon
$ foreman start -f Procfile-dev
Invoices allow a person to receive payment for goods or services in BTC. The invoice can be created in USD for a fixed price invoice or in BTC. USD invoices are converted to BTC at time of payment using the current exchange rate for BTC.
After an invoice is created, it can be viewed by going to the /invoices/:invoiceId route. For example:
http://localhost:8080/invoices/8c945af08f257c1417f4c21992586d33
Invoices have the following properties:
api_key
- The API key for Baron to verify that invoice creator is trusted [1]currency
- Currency of the invoice, can be either USD or BTCmin_confirmations
- Minimum confirmations before a payment is considered paidexpiration
(optional) - Expiration time for invoice (unix timestamp)webhooks
- (optional) An object containing event webhooks [2]metadata
- (optional) Container for arbitrary fields, the entire object is passed back to your app in the webhooks. This can be helpful for apps that do not track invoice ID's.id
- (special) If provided, a submission with an identical metadata.id will return the existing matching invoice instead of creating a new invoice.
line_items
- Array storing line itemsdescription
- Line item description textquantity
- Quantity of the item purchasedamount
- The unit cost of the line item [3]
NOTES:
- [1] The api_key is not stored with the invoice, it is just used for Baron to verify that the invoice creator is trusted. The api_key of the submitted invoice is compared against the
baronAPIKey
property in config.js. - [2] See the Webhooks section below for a more detailed description
- [3] Line item amounts are stored in whatever currency the invoice is set to.
An example of a new Invoice object:
var newInvoice = {
"api_key" : "268f84b93a69bbdf4c5f37dd67196eac75fdcda86dad301cc3fb4aed0670c2cb",
"currency" : "BTC",
"min_confirmations" : 3,
"expiration" : 1399997753000, // Optional
"webhooks" : { // Optional
"token" : "268f84b93a69bbd",
"paid" : { "url": "http://example.com/notifypaid" }
},
"metadata" : { // Optional
"id" : "someuser@example.com"
},
"line_items" : [
{
"description" : "Foo",
"quantity" : 2,
"amount" : 0.125
},
{
"description" : "Bar",
"quantity" : 1,
"amount" : 2.5
}
]
};
Invoices can be created by doing a POST of the newInvoice object to /invoices route. For example:
http://localhost:8080/invoices
Payments are created when the 'Pay Now' button on an invoice is clicked. User's are redirected to a view that displays the payment information such as amount due, address and QR Code for fulfillment of the invoice.
When a user's payment reaches the invoice's minimum confirmations, the payment is considered to be in the 'paid' status. Baron also handles other payment statuses:
Status | Description |
---|---|
Paid |
When the received payment fully pays off an invoice |
Overpaid |
When the received payment pays more than the invoice required |
Parital |
When the received payment pays less than the invoice required |
Unpaid |
Payments are unpaid when initially created |
Pending |
Payments are pending until they reach the invoices min confirmations |
Invalid |
Payments that have been reorged or double spent |
Payments can be viewed by going to the /pay/:invoiceId route. For example:
http://localhost:8080/pay/8c945af08f257c1417f4c21992586d33
Payments have the following properties:
invoice_id
- Invoice that this payment is associated withaddress
- Address to send BTC toamount_paid
- Stores the amount that was paid (Always stored in BTC)expected_amount
- Stores the amount that the payment expects to receiveblock_hash
- Stores the blockhash that the transaction was confirmed intospot_rate
- Stores the exchange rate at the time of paymentstatus
- The status of this payment (paid, unpaid, partial, overpaid, pending, invalid)tx_id
- Stores the transaction ID from bitcoindwatched
- Indicates if the payment is actively being watched by Baroncreated
- Time the payment was createdpaid_timestamp
- Time that payment became 'paid' statusreorg_history
- When applicable, contains the history of block hashes that the transaction was reorged out ofdouble_spent_history
- When applicable, contains the history of transaction ID's that double spent this payment
NOTE: This is just for reference, all payments are created and handled internally by Baron.
Baron is able to handle when a bitcoin transaction is reorged, double spent, or mutated. For example:
Baron is also able to handle partial payments. When a payment only partially fulfills an invoice the user can click the 'Pay Now' button again, this will create a new payment with the remaining balance. If the user has script enabled the payment page will automatically refresh with an updated remaining balance and payment address. Alternatively user's can also send multiple payments to the same address.
This is an example of an invoice that was paid in full by two separate payments:
Baron is capable of doing a POST to a url when a payment event occurs. A payment event is when a payment goes from one status to anther. If a payment was to go from unpaid
to paid
status this would trigger the webhook stored in newInvoice.webhooks.paid
. Here is a full list of supported webhooks:
var newInvoice = {
//...
"webhooks": {
"token": "93a69bbdf4c5f37dd6"
"paid": { "url": "http://example.com/notifypaid" },
"partial": { "url": "http://example.com/notifypartial" },
"invalid": { "url": "http://example.com/notifyinvalid" },
"pending": { "url": "http://example.com/notifypending" }
}
//...
};
token
- Secret token is posted to the webhook. This is typically used to authenticate the connection when Baron posts the status notification to the webhook. Identifying information about the Invoice may optionally be passed within themetadata
field, described above.url
- The url Baron should POST to when the payment event occurs
The app notified by the webhook can trust the incoming payment notification because it has a matching secret token that was set when the Invoice was created. Further information about the Invoice can optionally be queried from Baron via the /api/invoices/:invoiceId
route. For example, the Invoice can be verified as paid if is_paid
is true
.
Security Consideration
Both the webhook and payment status check can be subject to attack if intra-app communication is over the Internet without the protection of SSL. Verification with /api/invoices/:invoiceId
can successfully guard against a forged payment if at least the Baron side is protected by SSL. You can avoid these issues by communicating over an internal network or VPN between the two apps.
MIT