How I Made a Pay What You Want Form on typica.us
Over the years, there have been several times when someone has asked about sending money in support of ongoing Typica development and my answer was that they could drop a check in the mail and I'd be grateful for that. After all, this is a project that I've put a lot of my own time and money into and there are limits to what I can afford to cover on my own. Sometimes these people or their companies have written those checks, and I thank them, but more often those never show up. Earlier this year, I decided to create a little pay what you want form on this site where people could use a credit card with the expectation that making it easier for people to send money would result in fewer people asking how they can do that and more people just doing that. Over the past few months, that seems to have paid off. Five people or companies have used the form to send more money than this project has brought in for the previous nine years combined. While the project is still running at a financial loss for this year, this support has been incredibly helpful.
While working on this, I had some security concerns and wanted to make sure that I could implement this in a way that's safe both for anybody who might use the form and for the server. Looking at my server logs, I see automated attacks every day. These generally target PHP, Wordpress, and web based administrative interfaces for a variety of database and content management platforms. They're looking for servers that have not been set up properly or have not been updated to mitigate exploitable flaws in older versions of the programs under attack. As far as I know there have been no successful attacks in part because this site isn't running any of that stuff. It's serving static pages and I wasn't eager to add some huge piece of software with a poor security track record just to add one little bit of server side processing. My other concern was that I did not want my server to ever receive sensitive data like credit card numbers. If I don't have your data to begin with, I can't expose it in the event that a future attack is successful. I also wanted something that I wouldn't need to pay for unless people were using it.
Fortunately, I was able to come up with a solution that satisfies these concerns by letting Stripe handle the credit card processing side of things. The way this works, I have a form such as you see below.
You can view the source of this page to see what this is doing. There's a bit of JavaScript to take the amount you type in and convert it into cents before letting Stripe create another form to handle payment information. When you've finished entering your payment information, your browser talks first to Stripe's system over an encrypted connection. Stripe responds with a unique token that doesn't encode your data but can be used only once to finish processing the charge. That token and the amount you've chosen to pay get sent to my server, but your credit card information does not. Server side, I would need something that could grab the amount and token and use those to send its own encrypted message to Stripe to say, yes, go ahead and run this transaction. This program running on my server then reads the response to that, performs a little bit of logging so that if something goes wrong I have something to investigate, and generates a web page to let you know if that payment was successful or not. That receipt isn't saved anywhere on the server, but you can print it for your records if needed. I think I have my Stripe account set up so that you also get an email receipt from them. They send me an email when there's a successful transaction, but my code doesn't have anything to do with those messages.
Stripe provides a lot of documentation on how to do things server side. There are examples in Ruby, Python, PHP, Java, Node, Go, and .NET, but the problem here is that these are examples either in languages that I don't know well enough to be certain I'm not doing something stupid and/or require a lot of additional stuff on the server. I wasn't eager to figure out how to safely install and configure new software on the server and for something as small as this I also wasn't interested in brushing up on languages that I've never used for anything seriously and that I also haven't played with in a long time.
Fortunately, it also provides examples in curl. This is a common command line utility that I suspect is included in the documentation so that people can learn the Stripe API and see exactly what information needs to be sent and what information is received before they start writing code. I wasn't about to implement payment processing in shell scripts, but behind curl is a C language library called libcurl and it's trivial to convert the curl examples into code using libcurl. Typica is written in C++ and it uses C libraries to communicate with some of the hardware that Typica supports so this is well within my comfort zone.
This is how I decided that the server side processing would be done as a native executable written in C++. Not the most common choice for CGI programs, but one that should probably be considered more often.
Programs written to be run from a web server are a little bit different from most. The form is sent with an HTTP POST command. The web server receives this and sends the form data to my program on the standard input. The length of that input is passed in through an environment variable. That means the program needs to check that environment variable, convert the text into a number, and read that many bytes from standard input. The input is a URL encoded set of key value pairs so that needs to be decoded and the form fields split back out into separate pieces of data. In particular, I need the Stripe token and the number of cents to charge.
With that information extracted, it's time to use libcurl to send an encrypted message to Stripe with the required information and either receive the response or deal with any error that may have occurred.
The response from Stripe comes in the form of a big chunk of JSON. To report success or failure, I'd need to pull the relevant information out of that response. I decided to use RapidJSON for this instead of hacking together something on my own. I only needed to write one small convenience function to pull out the data I needed from the response.
I wanted to log these responses so that if anything went wrong I'd have something server side to investigate. Since I was already using PostgreSQL for other things on the server, I created a new database and a new role for accessing that database, keeping permissions set so that a service that accesses one database on the system can't gain access to unrelated databases. To connect to that database and create new log entries I just used libpq.
With the response processed and logged, it's time to generate the web page sent to report success or failure to the person who was trying to send money. This is just a matter of writing an HTTP Content-type header, an empty line, and the page HTML on the standard output. To see that response, scroll back up and use the form to send money in support of ongoing Typica development.
That's all there is to it. With the program compiled, the resulting executable moved to a special location where the web server will look for it, and a tiny bit of server reconfiguration to enable that, I now have this little pay what you want form backed by a tiny server side program.