Showing posts with label bitcoin. Show all posts
Showing posts with label bitcoin. Show all posts

Monday, May 25, 2015

Using the Coinbase Merchant API with Node

I've been writing about different pieces of my Bitcoin integration since last week when Wednesday's comic broke the Fourth Wall while building a paywall. First I explained the client-side code that handled the 402 response. Then on Friday I gave an outline of my Express middleware that controls the access to non-free resources. I used several stubs and mock objects in that post because the actual implementations of the pieces will vary based on where they are integrated - it's just not very interesting what I did with my database access because it's probably different from yours.

But one last part in which people may be interested is the integration with Coinbase's Merchant API via their Node module. I used Coinbase in my implementation because their API was very easy to work with and the integration points are extremely modular. It is set up in such a way that I could easily replace Coinbase with another service such as BlockCypher or calls to the Blockchain API. But I had a wallet with Coinbase already so I went with that...

There are really only two interactions with Coinbase that I perform. One is creating a new Bitcoin address that is tied to my Coinbase wallet. The other is processing the callback from Coinbase informing my server that payment has been made.

Creating a new address is very simple if you've set up your Coinbase API access correctly. Log in to your Coinbase account and go to https://www.coinbase.com/settings/api to create a new API key. Make sure it has the "address" permission and it is enabled. You have to verify every step with 2-factor authentication so have your phone nearby. If possible, set the IP restriction on your key as an extra precaution against unauthorized access.

Your key has two parts, the key and the secret. After creation, you can copy them and use them with the Coinbase Node module. Keep them secure! Don't put them directly in your code and accidentally push it to a public repo on GitHub! Besides the key and secret, you'll also need the Coinbase account number for which you want to generate addresses. Your account number is basically just a Coinbase identification for each wallet you have with them. It's displayed as part of the URL when you are looking at your wallet on their web site.

Once you have those three things, to generate a new address you do something like this:

var Client = require("coinbase").Client;

var client = new Client({
    apiKey: "your-coinbase-api-key",
    apiSecret: "your-coinbase-api-secret"
});

var accountId = "your-coinbase-account-id";

var Account   = require("coinbase").model.Account;
var myBtcAcct = new Account(client, {
    "id" : accountId
});

var secretCode = "secret-code-unique-to-each-address";

var args = {
    callback_url: "http://example.com/callback?secret=" + secretCode,
    label: "your address label"
};

myBtcAcct.createAddress(args, function(err, data) {

    if (err) {
        console.log("unable to create address: " + err);
    } else {
        console.log(data.address);
    }

});

The basic steps are to create a client, create an account object with that client, and then call createAddress on that account. The createAddress function takes an arguments object as the first parameter, in which you specify an optional callback URL and label. The label is just some text you can add for your own reading later. It has no functional aspect. But the callback URL is very important. As you can see on line 18, I use a secret code as part of the callback query string. In my example it is hard-coded, but in real life you'll want to generate a different one for every address. If you keep the secret code only on your server - never send it to the client in any way - it ensures that someone won't be able to send their own POST to your callback URL and trick your server into thinking that they have paid. Store the address and secret code in your database and make sure they match in your callback processor. If they don't, something is wrong and you shouldn't honor the validity of the callback!

If you wanted, you could take the code above and plug it into the example app from Friday's post to generate real addresses. If you wanted to handle the real callbacks in that app as well, you'd want to be able to process the actual data format that comes from Coinbase with those POSTs. The POST to your callback URL will contain this as the body:

{
  "address": "somebitcoinaddress",
  "amount": 1.234,
  "transaction": {
    "hash": "somebiglonghashvaluething"
  }
}

For me, the most important parts were the secret code in the callback query string, the Bitcoin address from the body, and the amount from the body. I don't need the transaction hash, but other applications might. The two pieces of information from the POST body are easy to extract if you use the body-parser middleware for JSON. Here is an example of an app that just takes the callbacks - you could substitute the callback route from Friday's post with this one if you want.

var express = require("express");
var bodyParser = require('body-parser');

var app = express();
app.use(bodyParser.json());

app.get("/callback", function(req, res, next) {

    var secretCode = req.query.secret;
    var addr = req.body.address;
    if (addr) {

        var amountRecieved = req.body.amount;

        // make sure the secret and address
        // match your records, and if so
        // mark the transaction as paid if
        // the amount >= the price.

    } else {

        console.log("something went wrong!");

    }

    // always give coinbase a 200
    res.sendStatus(200);

});

// ------------ start listening
var server = app.listen(3000, function() {
    console.log("listening on port %d", server.address().port);
});

One thing to note (that I missed at first) is that a customer doesn't have to pay the full amount in a single transaction. If you were charging 0.05 BTC, it would be perfectly fine to get two callbacks, each indicating a payment of 0.025 BTC. When handling the data, make sure you add up amounts sent to the same address instead of just taking the latest.

I think I've covered all the major parts of my paywall function now. Just to be clear, I'm not actually advocating everyone putting up paywalls on all their content. Last week's comic was just a joke that happened to include a novel way to handle online micropayments. I do believe that there are lots of legitimate use cases for Bitcoin microtransactions on the web, particularly for content creators such as myself. Charging a few cents worth of cryptocurrency here and there for a pieces of premium content or special features could be a viable alternative to displaying ads on the web (tipping is another alternative, which I've discussed previously).

So enjoy today's comic - completely free!

Amphibian.com comic for 25 May 2015

Friday, May 22, 2015

Making Bitcoin Paywall Middleware

Today's comic doesn't include a lot of built-in technology like Wednesday's did. Just a moth joke. Or was that just a mistake? I'm not sure. Moving on...

Wednesday's comic had a Bitcoin paywall, and in Wednesday's blog I explained how I processed a 402 response in the client to make it work. But the server played more than a bit part in the whole operation. Get it? Bit part. Bitcoin. Moving on...

It's like regular money, but with more math.
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 response along with the special payment instruction headers.
Alternatively, the datastore might find a record for this resource and client. That means the client has been here before. The record returned by the datastore will indicate whether or not the payment is complete. If the payment has been made, the middleware allows normal access. If payment has not been made, the middleware returns the 402 response to the client with the special payment instruction headers. The data for those headers comes out of the payment record.
At any time, an external Bitcoin processing system can determine that payment has been made to one of the addresses generated by the address generator. It sends data on the payment transaction to the payment callback resource which looks up the record in the datastore and marks it as paid.

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) {
        if (secret === demoRecord.secret) {
            callback(demoRecord);
        } else {
            callback(null);
        }
    }
};

function newBitcoinAddress(productCode, callback) {
    callback(null, demoRecord);
}

function send402(res, data) {

    res.setHeader("X-Payment-Types-Accepted", "Bitcoin");
    res.setHeader("X-Payment-Address-Bitcoin", data.address);
    res.setHeader("X-Payment-Amount-Bitcoin", data.cost);
    res.sendStatus(402);

}

function paywallMiddleware(req, res, next) {

    // let's use the URL as the product code,
    // since that's really what is being sold
    var productCode = req.path;

    datastore.findRecord(req, productCode, function(recordId) {

        if (recordId) {

            // found a record, which means that this client
            // has a payment address and purchase record already.
            // now check to see if it has been paid.
            datastore.checkPaidStatus(recordId, function(data) {

                if (data.paid) {

                    next(); // all paid, move along...

                } else {

                    // respond with the payment address that already exists
                    send402(res, data);

                }

            });

        } else {

            // no record found, which means a new Bitcoin
            // address and purchase record must be created.

            newBitcoinAddress(productCode, function(err, data) {

                if (err) {
                    next(err);
                } else {

                    datastore.newRecord(data);
                    send402(res, data);

                }

            });

        }

    });

}

app.get("/free", function(req, res, next) {
    res.send("free content");
});

app.get("/paid", paywallMiddleware, function(req, res, next) {
    res.send("paid content");
});

app.get("/callback", function(req, res, next) {

    var secretCode = req.query.secret;
    datastore.findRecordBySecret(secretCode, function(record) {
        if (record) {
            record.paid = true;
        }
    });
    res.sendStatus(200);

});

// ------------ start listening
var server = app.listen(3000, function() {
    console.log("listening on port %d", server.address().port);
});

That looks like a lot of code, but it's not really that complicated. Near the top, you see I have some mock objects for the demo. In a real scenario, your datastore object would access an RDBMS or the file system to store and retrieve actual purchase records. Here, though, a single fake record and some stub functions will suffice. The newBitcoinAddress function is also a stub - in the real app, this is where I use the Coinbase API to create addresses and associated purchase records each with their own unique secret code as part of the callback URL. That 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 to http://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?

Amphibian.com comic for 22 May 2015

Wednesday, May 20, 2015

Using HTTP 402 for a Bitcoin Paywall

Bitcoin. Perfect for Microtransactions?
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 using the HTTP 402 response code.

Response code 402 is officially "reserved for future use" but I felt that it was about time the future showed up. It's 2015. Where is my moon colony?? Anyway, 402 means "Payment Required" and was apparently intended to be a way for web servers to indicate to clients that the requested resource had to be purchased. Unfortunately, a way to pay for those resources was never worked out and the response code has languished in the realm of TBD for many years.

But these days we have Ajax, REST web services, and Bitcoin. It's all coming together. People have kicked around the notion of integrating 402's with Bitcoin transactions for a few years now but no significant implementation has emerged. With today's comic, sadly, nothing has changed.

I did, however, create a functional web content paywall for Bitcoin microtransactions. Here's how it works...

When a client - be it a human-operated web browser or another computer program - accesses a URL which does not dispense free content, the server will return a 402 response instead of the content. That response also includes three special headers, examples of which are shown here:

X-Payment-Types-Accepted: Bitcoin
X-Payment-Address-Bitcoin: putarealbitcoinaddresshere
X-Payment-Amount-Bitcoin: 1.234

The first, X-Payment-Types-Accepted, should be a comma-separated list of acceptable payment types. In my example and in my comic I am only accepting Bitcoin for now, although a similar form of payment such as Litecoin would easily be possible in place of or in addition to Bitcoin. Each item listed in this header should be used to check the other 2 headers. The X-Payment-Address-XXX header specifies an address to which the payment should be sent. The X-Payment-Amount-XXX header specifies how much this particular resource costs.

The key is that the last part of the address and amount headers should match an item in the list of types accepted. If I wanted to accept either Bitcoin or Litecoin, my headers might look like this:

X-Payment-Types-Accepted: Bitcoin, Litecoin
X-Payment-Address-Bitcoin: putarealbitcoinaddresshere
X-Payment-Amount-Bitcoin: 1.234
X-Payment-Address-Litecoin: putareallitecoinaddresshere
X-Payment-Amount-Litecoin: 2.345

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-Address-Bitcoin");
                var cost = xhr.getResponseHeader("X-Payment-Amount-Bitcoin");

                var bcurl = "bitcoin:" + addr + "?amount=" + cost;
                var bcqr = encodeURIComponent(bcurl);

                if ($("#paydiv").html() === "") {
                    $("#paydiv").html("<p>Pay with Bitcoin!<br>" +
                                      "<a href='" + bcurl + "'><img src='/qrc?text=" + 
                                      bcqr + "'></a><br>" +
                                      "Send " + cost + " BTC to<br>" +
                                      addr + "</p>");
                }

                setTimeout(checkForPayment, 2000);

            } else {
                console.log("failed");
            }

        }

    });

}

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 whole checkForPayment() 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!

Amphibian.com comic for 20 May 2015

Friday, February 6, 2015

Now Working for Tips

I'm trying something new this week. In the past, you may have read my posts about how I have no Bitcoins and how I lament the fact that the Internet is all advertisements.

I may have found a solution to both of those problems in ChangeTip. ChangeTip allows you to tip content creators on the Internet using Bitcoin. My comics have Facebook "Like" buttons on them, but liking a comic doesn't help me pay for the web server. With the ChangeTip "Tip" button, you can easily give me a dollar or two if you like my work.

Here's how I use it. I added the "Tip" button to the bottom of my comic page. When you click on it, a popup gives you the option to send me a tip out of your ChangeTip account or directly with Bitcoin. While you can tip in US dollar amounts, the tips are all converted to Bitcoins in my account.

There's the tip jar.

But there's more to this service than a Tip button. I haven't tried it yet, but you can also tip people via any social network just by mentioning both ChangeTip and the recipient in a post. For example, If you and I have both connected our Twitter accounts to ChangeTip, all you'd have to do in order to tip me $1 would be to tweet "hey @THECaseyLeonard, here's $1 @changetip" and I'd get a dollar's worth of bits from your ChangeTip account sent to mine. There's similar behavior on Facebook.

This seems like an interesting concept and, in my opinion, a good alternative to advertising as a way to make a few dollars as a content creator. My comic runs no ads, but my blog here does have some banners on it. In a good month, I'll earn maybe $2 from them. If people like just one or two comics per month enough to tip, I could easily replace that revenue.

The other benefit is that the money is more directly related to how people feel about the content I create. If I make $0.15 from an ad click here, it's not because someone really liked this blog post - it's because Google showed them an ad for something in which they were interested. If someone sends me a $0.15 tip, it's because they liked my blog post. The tip makes me feel better about the work I'm doing.

While I think the Tip button is a positive addition to my site, it is not perfect. It fits in nicely with the other social media buttons, but when you click on it the controls appear in a floating IFrame whereas the others typically use a separate pop-up window. I wouldn't mind that so much, except that it always expands down and right, which throws off the rest of the page - especially on mobile. Perhaps a mobile-optimized view would be in order.

I do feel a bit like the guy playing the saxophone in the subway station waiting for passers-by to toss change in a hat, but he and I have a lot in common I suppose. I guess I'll see what happens. It is a relatively new service, and my comic doesn't have a large readership, so I don't expect tips to start pouring in right away. Hopefully, though, the concept catches on and this service or one like it can usher in a new era of how the Internet is funded.

Amphibian.com comic for 6 February 2015

Monday, October 20, 2014

Follow the Scrip

Today's Amphibian.com comic represents the intersection of three very different issues. A trivium, if you will. That's the singular form of trivia. And trivia are 3-way intersections. Seriously, "trivia" is a place where three roads meet. It's Latin. Tri = 3, Via = road.

So what were these three things that came together in my mind and inspired me to create such a comic?

One was the recent surge in cryptocurrencies. It's like Bitcoin got some attention in the mainstream-ish media and then everybody was coming up with their own Whatevercoin. Litecoin. Dogecoin. Peercoin. Darkcoin. There's even Pandacoin. Why not Frogcoin? You get the idea. Everybody was making up coins. Maybe they felt like since they missed the boat on Bitcoin, they should start mining something else (or maybe everything else) just in case one of the others ends up being the winner. I guess it could happen. It happens with other things. For example, Google wasn't the first search engine, just the best. Sorry Bing.

The second thing was companies paying employees with debit cards instead of checks or direct deposit into checking accounts. It happened back in 2013 in a couple places and it really troubled me. Basically, workers were getting paid with those Visa pre-paid cards that you can use like credit cards to buy things. The problem was that there were sometimes fees associated with their use, and it can be tricky to spend 100% of those things. If you've ever had one you know what I mean. Workers were basically being cheated out of some money. The reasons that employers were doing this seemed a little shady. Some lawsuits were filed, and thankfully the government ruled that employers can't require you to get paid that way. They still have to give you the option of regular pay. I'm glad they did that, because I was concerned with the natural progression of the idea if it had been left unchecked.

And by progression, I mean regression. The third thing, which is tangential to the second, is the concept of company scrip. I thought, "what if Wal-Mart decided to pay its employees with Wal-Mart gift cards?" It seemed like a real possibility. Then I found out that it actually happened! At least in Mexico. Fortunately, the Mexican Supreme Court put a stop to it. But what if they try it someday in the United States? Then we'll be back in the days of the mining companies paying their employees in fake money that was only good at the company stores, which sold everything with huge mark-ups and kept the employees constantly indebted to the company. That practice ended many years ago, and I do not want to see it return. But wait, mining companies? That ties back to the cryptocurrency thing, since people "mine" for the coins! Whenever I find a loop like that, I know there has to be at least one comic I can make out of it.


Therefore, the end result of my ponderings on these issues was the comic in which frogs are working in a cryptocurrency mine and getting paid in a different and more obscure cryptocurrency which is only good in their company store. I'm fairly certain that Merle Travis would write a song about that exact scenario if he were still alive. It would probably be titled Sixteen Gigabytes.

Amphibian.com comic for 20 October 2014

Wednesday, October 15, 2014

My Thoughts on Bitcoin

In today's comic, the frogs are getting in on the Bitcoin craze. I've stayed out of it personally. It seems like an interesting concept and all, but I have a few reservations at this point. I won't say that I'm opposed to it, because I'm not really. I just don't think it's for me yet.

First, it's because I'm a person. I'm not a business. I have nothing to sell, so I'm not trying to reduce the fees I pay for credit card transactions or anything. I'm not trying to get more customers who might not want to use U.S. Dollars. I'm just a person.

Second, it's because I don't need to. All the places I buy groceries still take normal American currency. I live in central Pennsylvania. I don't know where the nearest business is that accepts Bitcoin, and I don't know if there are any in the state that accept it exclusively. I'm too practical to try to use Bitcoins at this point.

Third, it's too volatile. I don't have the hardware needed to mine the coins, so to get any I'd have to buy them with my dollars. I might see their value double in the next month, or I might see their value halve. I'm just not into currency speculation. I'll take my chances with U.S. inflation.

So that's why I don't have any Bitcoins. I do have a Bitcoin wallet somewhere though - if you want to send me some I won't say no.

Amphibian.com comic for 15 October 2014