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.
- Responsive web design allows for a better user experience across multiple devices.
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the
above copyright notice and the following two paragraphs appear in
all copies of this software.
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
Clone the repository:
$ git clone https://github.com/slickage/baron.git
$ cd baron
$ npm install
Options are read from environment variables listed in the tables below. A common way to configure environment variables and launch node applications is using foreman with a .env file. Alternatively node applications are often launched with a systemd .service definition. foreman export
can generate systemd files from an .env
file.
DB_HOST
- CouchDB's connection hostname (do not specify protocol)DB_NAME
- The name of Baron's databaseDB_SSL
- Set to true if couchdb is configured to use SSLDB_USER
- If configured, the database admin usernameDB_PASS
- If configured, the database admin's password
BITCOIND_HOST
- Bitcoind hostnameBITCOIND_PORT
- Bitcoind RPC portBITCOIND_USER
- RPC username (set in bitcoin.conf)BITCOIND_PASS
- RPC password (set in bitcoin.conf)
BARON_API_KEY
- Secret api key, used to post invoices to Baron [1]PORT
- Baron listens on this TCP portTRUST_PROXY
- Set totrue
if Baron is behind a reverse proxy, see note belowPUBLIC_URL
- Should match Baron's public URL (protocol, hostname and optional port)ADMIN_EMAILS
- Comma separated list of Baron admin email addressesSENDER_EMAIL
- Outgoing email from Baron use this addressSMTP_HOST
- SMTP Host for sending outgoing emailSMTP_USER
- SMTP login usernameSMTP_PASS
- SMTP login passwordSMTP_PORT
- SMTP port (default 465)
APP_TITLE
- Default title in invoices and payment views (default to 'Baron', can be overridden per-invoice)CHAIN_EXPLORER_URL
- Address prior to /txid in explorer (defaults to blockr.io)MIN_BTC
- Minimum BTC amount for invoice line items (default 0.00001 BTC)MIN_USD
- Minimum USD amount for invoice line items (default 0.01 USD)SPOTRATE_VALID_FOR_MINUTES
- BTC/USD exchange rate frozen for this duration (default 5 minutes)TRACK_PAYMENT_UNTIL_CONF
- Stop watching payments for double-spend (default 100 confirmations)
NOTES
- Most properties have sane default values, see config.js for defaults.
- The
BARON_API_KEY
is a shared secret between Baron and applications who post invoices. - Support for USD invoices is experimental at this time. If payments for fiat invoices are made while Baron is down, it cannot retrieve an accurate exchange rate when starting back up. Baron will record the latest exchange rate it was aware of before going down.
You must use a reverse proxy as you will need SSL to protect the security of Baron. nginx proxy_pass
is most frequently used for this purpose. This example nginx configuration snippet forwards requests to a SSL virtualhost to Baron running on localhost:8080.
server {
listen 443;
server_name baron.example.com;
ssl on;
ssl_certificate_key /etc/pki/tls/private/baron.example.com.key;
ssl_certificate /etc/pki/tls/certs/baron.example.com.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
When using a remote proxy you must enable Baron's TRUST_PROXY
option. Baron will then be able to ascertain the client IP address from the X-Forwarded-For header.
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
NOTES
- Baron is entirely reliant upon walletnotify to learn of transactions. Be sure to customize the two instances of
api_key
within bitcoin.conf to match theBARON_API_KEY
configuration of Baron. Additionally the/walletnotify
and/blocknotify
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. Note that the environment variables must be set by wrapper shell script, systemd EnvironmentFile
or foreman .env
file.
$ node server.js
Running Baron with foreman and nodemon
$ foreman start -f Procfile-dev
Baron makes an effort to support USD-denominated invoices and to record the USD-value at the time of the transaction to help facilitate business accounting records. These features work relatively well but with caveats.
- Baron has two issues described below when dealing with payments that happened during downtime. While these issues are hypothetically problematic, in practice it may only rarely happen because downtime is extremely rare and a user is highly unlikely to pay when they are unable to load the payment page.
- Baron has no way of knowing the true time when the 0-conf payment actually occurred if bitcoind was down. The best Baron can do is to guess from the blocktime. This is problematic because as the Bitcoin protocol does not guarantee that the blocktime is true, and even if it were true it could be an indeterminate time after the actual 0-conf payment. (We could consider querying one or more explorers to obtain the transaction's first seen time. We are hesitant to add additional external data sources as it would complicate the code for a rare corner case.) TODO: Baron does not yet actually use the transaction blocktime when recovering from downtime, this will be fixed in Issue #56.
- Since Baron was not recording ticker rates during downtime, it has no way of knowing the exchange rate at the time of the payment. In general Baron uses the most recent ticker rate prior to the transaction time. This could be substantially different from the true exchange rate if downtime was long.
- The Volume Weighted Average may differ from your idea of the appropriate market price at a given moment. Baron grabs the USD/BTC exchange rate every 5 minutes from Bitstamp's API. For the sake of simplicity it uses Bitstamp's volume weighted average price (vwap). The vwap changes slowly so querying every 5 minutes is usually adequate for the purpose of minimizing the rate growth of the ticker database. The trouble with this is at times of major market movement the vwap may differ from what people consider to be an appropriate market price. We are willing to consider alternative methods to obtain price data or to allow supplying your own method in configuration.
Posting an invoice to Baron or the webhook callbacks to inform apps of payment activity can expose shared secrets to hostile attackers if that intra-app communication is trasmitted without encryption over a public network. An eavesdropper who steals the api_key
can cause trouble for the Baron deployment. If an attacker steals the webhook token
they could possibly fool the application into believing a payment has occurred when it really did not.
Ideally the app and Baron would be on the same local network. If separated by a public network you are highly advised protect the communication between the two apps with HTTP SSL or VPN.
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 paidtitle
- (optional) Title to display at the top of invoicestext
- (optional) Text to display at the bottom of invoices, may include HTML linksexpiration
- (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
Baron is able to handle when a bitcoin transaction is reorged, double spent, or mutated. Admins will be emailed if Baron detects an invalid transaction. This is an example of an invoice with an invalid transaction:
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 another. 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 contains a matching webhook 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
. Note that this intra-app communication can be at risk if transmitted unencrypted over the Internet.