Professional Documents
Culture Documents
Features
Full Test coverage.
Support for both API Key + Secret and OAuth 2 authentication.
Convenient methods for making calls to the API.
Automatic parsing of API responses into relevant Javascript objects.
Adheres to the nodejs error-first callback protocol.
Continuous Integration testing against node 0.10, 0.11, and 0.12.
Installation
npm install coinbase
Version Compatibility
Version GitHub repository
1.0.x
This repository
0.1.x
mateodelnorte/coinbase
Npm coinbase package name used to refer to the unofficial coinbase library
maintained by Matt Walters. Matt graciously allowed us to use the name for
this package instead. You can still find that package on Github. Thanks, Matt.
Quick Start
The first thing you'll need to do is sign up for coinbase.
OAuth2
If you're writing code that will act on behalf of another user, start by creating a
new OAuth 2 application. You will need to do some work to obtain OAuth
credentials for your users; while outside the scope of this document, please
refer to our OAuth 2 tutorial and documentation. Once you have these
credentials, create a client:
var Client = require('coinbase').Client;
var client = new Client({
'apiKey'
'apiSecret'
: mykey,
: mysecret,
'accessToken' : accessToken,
'refreshToken' : refreshToken
});
});
Sending bitcoin
var args = {
"to": "user1@example.com",
"amount": "1.234",
"notes": "Sample transaction for you"
};
account.sendMoney(args, function(err, txn) {
console.log('my txn id is: ' + txn.id);
});
Requesting bitcoin
var args = {
"from": "user1@example.com",
"amount": "1.234",
"notes": "Sample transaction for you"
};
account.requestMoney(args, function(err, txn) {
console.log('my txn id is: ' + txn.id);
});
Listing current transactions
account.getTransactions(null, null, function(err, txns) {
txns.forEach(function(txn) {
console.log('my txn status: ' + txn.status);
});
});
Checking bitcoin prices
client.getBuyPrice({'qty': 100, 'currency': 'USD'}, function(err, obj) {
console.log('total amount: ' + obj.total.amount);
});
Error Handling
Errors are thrown for invalid arguments but are otherwise returned as the first
argument to callback functions.
Errors contain type properties so that you can route the error to the right
handler code. The possible types are:
Type
Description
AuthenticationError
returned if there was an authentication error
Type
Description
returned when the current access token is no longer
InvalidAccessToken
valid
ExpiredAccessToken
returned when the current access token is expired
returned when there is a failure refreshing the access
TokenRefreshError
token
returned when a user's Two Factor Auth token needs
TwoFactorTokenRequired
to be included in the request
returned for errors related to interacting with the
APIError
Coinbase API server
acct.getBalance(function(err, bal) {
switch (err.type) {
case 'ExpiredAccessToken':
// use the client.refresh API to get a new access_token
break;
case 'InvalidAccessToken':
//handle error
break;
case 'AuthenticationError':
//handle error
break;
}
});
Errors are always of the type Error and can print a trace:
acct.getBalance(function(err, bal) {
if (err) console.log('Where did this error come from?\n' + err.stack);
});
Errors that are related to an http request will have a response field with the
entire http response:
acct.getBalance(function(err, bal) {
if (err) console.log('http error code: ' + err.response.statusCode);
});
Testing / Contributing
Any and all contributions are welcome! The process is simple:
1. Fork this repo
2. Make your changes and add tests
3. Run the test suite
4. Submit a pull request.
Tests are run via mocha and nock. To run the tests, clone the repository and
then:
npm install
npm test
You can also run the tests against various node environments using the
Dockerfile.example file.
1. cp Dockerfile.example Dockerfile
2. edit Dockerfile and uncomment the node version that interests you
3. [sudo] docker build -t coinbase-node .
4. [sudo] docker run -it coinbase-node
More Documentation
You can generate html documentation by running the command:
npm install && npm run docs
Open generated html files in docs/ dir with a browser.
To handle the Bitcoin payments and unlock the content, there are several pieces at work. I am
using the Express framework for my web application, so I have a middleware applied to each
non-free resource. There is also a Bitcoin address generator to create new payment addresses.
Another resource serves as the payment notification - when payment is made to an address, this
resource receives data about the transaction. Finally, there is a datastore that manages the
records to keep all the other pieces in sync.
This is how it works:
First, a resource is requested which requires payment. The middleware checks with
the datastore to see if the requesting client has an existing record for this resource.
If no existing record is found, it means the client has never attempted to access this
resource before. The middleware uses the address generator to create a new
payment address record and gives it to the datastore. The client is returned the 402
I used the Coinbase Merchant API as the external Bitcoin processing system. When I create
addresses with Coinbase, I can specify a callback URL for each one and Coinbase will POST
some data to it when payment is made. Each address gets a unique URL so my system can look
up the payment record that matches it. I am using the official Coinbase Node module in my app.
Here is a basic outline of the app:
var express = require("express");
var app = express();
// mock purchase record
var demoRecord = {
recordId: "12345",
code: "product-code",
paid: false,
cost: 123.45,
address: "bitcoinaddress",
secret: "itsasecret"
};
// mock datastore object
var datastore = {
findRecord: function(req, productCode, callback) {
callback("12345");
},
checkPaidStatus: function(recordId, callback) {
callback(demoRecord);
},
newRecord: function(data) {
// store the record
},
findRecordBySecret: function(secret, callback) {
secret code should never be returned to the user, but needs to be stored so the record can be
retrieved later in the callback handler. The send402 function is just a utility function that sets the
special response headers and sends the 402 code.
The paywallMiddleware function is the implementation of the algorithm I described above. It uses
the mock datastore and address generation function.
The routes at the bottom allow testing the system. Launch the app and point your browser
tohttp://localhost:3000/free. You should get the free content. Now try
http://localhost:3000/paid . You should get a 402 response. This is because the /paid route
includes the paywallMiddleware. To get access, you'll need to pay.
This demo doesn't require any real Bitcoin transactions to take place. To simulate the callback
coming from Coinbase, I included a /callback route that is a GET, which makes it easy to test
from your browser. In real life, this will be a POST. To simulate sending payment to the address,
you just have to go to http://localhost:3000/callback?secret=itsasecret. When you do, you
should get a simple OK response...but on the server side, the demo record was updated to paid
because the secret code matched. Now you can go back to http://localhost:3000/paid and get
the paid content. If you had used a different (incorrect) value for secret, the /paid resource would
not have been unlocked.
Next week I'll go into a little more detail about how I use the Coinbase API. My hope is that
someday more content creators such as myself can use functions like this for micropayments in
place of running advertisements on their sites.
But that's just a dream right now. How long will it take to become reality? A few moths? I mean,
months?
The punchline for today's comic is hidden behind a paywall. It is a literal wall, built by the frogs to
hide the last frame of the comic. However, if you have some Bitcoin to spare (it costs 0.001
coins, about $0.25 US at the present time) you can actually pay for the wall to be removed.
A fair amount of interesting work went in to this comic, but it all revolves around the concept of
It's then up to the client to deal with this response. Today, browsers will silently ignore this extra
information and just tell you that the response code means "Payment Required" without letting
you know how to pay. In the future, browsers might prompt you to submit the payment (see this:
Zero Click Bitcoin Micropayments). Today, though, you have to do things manually.
In certain cases, the server could include the Bitcoin address and price in the HTML that
accompanies the 402 response. Since I am just requesting the paid resources via Ajax, I didn't
bother with that and don't include anything human-readable in the response. Here is how my
client-side code works when you view the comic:
function checkForPayment() {
$.ajax({
url: "/paidContent/paywall-comic",
dataType: "html",
success: function(data) {
$('#comicArea').html(data);
},
error: function(xhr, respText, et) {
if (xhr.status === 402) {
var addr = xhr.getResponseHeader("X-Payment-AddressBitcoin");
Bitcoin");
var bcqr = encodeURIComponent("bitcoin:" + addr + "?
amount=" + cost);
if ($("#paydiv").html() === "") {
$("#paydiv").html("<p>Pay with Bitcoin!<br>" +
"<img src='/qrc?text=" + bcqr +
"'><br>" +
}
});
}
When the free version (missing the last frame) of the comic loads, checkForPayment() is called
and attempts to get the data for the complete comic from the URL /paidContent/paywall-comic.
Anything on my site with a path starting with /paidContent/ will require purchasing. On a
successful response, which you'll only get if you've paid, the unpaid version of the comic is
replaced with the data from the server.
The interesting part is the error response handling on line 11. A 402 response is an error
because it is in the 400 range - indicating a client-side error. It's not a very serious error - just
asking for something for which you have not paid! I am using jQuery so the first parameter
passed to the error handler function is the jqXHR object. There are many things you can do with
that object, but I only need to check the response status to see if it is a 402, and if so I read the
values of the X-Payment-Address-Bitcoin and X-Payment-Amount-Bitcoin headers. Yes, I
am ignoring my own X-Payment-Types-Accepted header because I happen to know that it
only contains Bitcoin (that's the only kind of coin I have right now). If I expected other types, the
right thing to do would be to read the X-Payment-Types-Accepted header and loop over the list
of values to get the names of the other headers.
I take the address and price and submit them to my QR Code image generation URL to make an
easy, scannable way to pay - but also just display the address and price to the user. Again, I'm
doing this on the client side because I'm requesting the data with Ajax. If the user navigated
directly to a URL which required payment, the server could have responded with that HTML as
part of the 402 page, much in the same way servers respond with custom (and sometimes
helpful) 404 pages today.
The last part is to check the /paidContent/paywall-comic URL again to see if a payment has gone
through. Since it can be a few seconds before Coinbase tells my server that payment has been
sent to the address, I took the quick and dirty route of setting a timeout and running the
wholecheckForPayment() function again. I could have connected a Websocket to wait for
payment confirmation from the server if I wanted to get fancy - a website that was expected to
get more traffic than my comic would probably want to go that route instead. A possible future
enhancement would be to include an additional header with a recommendation for the client specifying if it should refresh, wait, redirect so some other URL, etc.
In the future, I might put more special features on Amphibian.com on the /paidContent/ URL,
instead of it just being part of the joke. Maybe special comics that you can only access by paying
a few cents? I'm not sure yet, but I tried to develop a framework that robust enough to support
future expansion. At this point, I can easily make the server request payment for anything thanks
to the Express middleware that I wrote combined with the Coinbase Node module. But that's too
much for a single blog post so I'll talk about that on Friday!
I also hope that more web sites adopt and expand on this model now that I have it working in a
semi-legitimate application. In my opinion, it beats displaying ads for enabling content creators to
monetize. You can help me monetize by paying to get the whole comic today!