How To Accept Bitcoin Payments
Bitcoin is gaining popularity, and last week I decided to implement it as a payment option for my computer-generated music service. Why I decided to do that is detailed here (payment is only needed for commercial use of the music), but here I’ll share the technical details for implementing the bitcoin integration.
First, you need to pick a payment gateway/provider. You can pick one from this list. They differ in terms of their API and specific flow, but the general flow should be almost the same. Some of the implementation details below are relevant only to what I picked up – Coinbase – but they should be similar for other providers. I picked Coinbase, because it seemed the most developer-friendly and straightforward. It has one bug that I had to workaround, but apart from that it’s ok.
The general flow (probably containing some Coinbase specifics) is below. Note that I am describing the most complex scenario, where you have multiple items with variable prices and quantities. You can use the html/js code I used in my cart page. On the other hand, it’s assuming a best-case payment provider that does not redirect the user to external pages and uses simply a javascript dialog.
- Place a custom “Pay with Bitcoin” button on the checkout page
- Clicking it submits a form and thus triggers a back-end checkout process
- The back-end code saves a new purchase/order in the database and returns its id
- Then the back-end code invokes the payment gateway API, providing the order id (or any custom application data) and the price, and gets a code to identify the transaction
- The price can be provided in BTC or any currency. Depending on the API, you may want/need to convert a fixed price in a popular currency to BTC before passing it via the API. If you do so, be sure to cache the conversion rates for a while – no need to fetch them every time. Providers may do this conversion automatically, but if you want to display it to the user first, you may want to do the conversion yourself.
- On returning the code to the front-end, the javascript gets it, and opens a dialog (using a js function/event provided by the provider).
- The dialog may contain multiple payment options, but it usually shows a bitcoin payment address to which the user should send the money. He can do that with his mobile or desktop wallet
- After the transaction is completed, a callback URL is invoked on your application, which contains the order id that you passed when generating the code. Then you can confirm the purchase and send the items purchased.
- Then the dialog is automatically closed (or the user presses “transaction complete”). At that point you need to send an (ajax) request that clears the cart contents, and redirect to a “Thank you” page.
Note that the documentation of the payment provider should provide all the implementation details. Below I’ll share some specifics of my case: Coinbase and Java:
The default scenario described in the docs is to put a button with a predefined code on the page. That doesn’t work if you need to increase quantities or have multiple items. That’s why you should dynamically generate the button code. But the javascript library only handles this on page load. So I had to copy some minified javascript code and invoke it when the code is returned from the back-end. Here is the whole javascript code (invoked when the user presses “Pay with bitcoin”):
$(document).ready(function() {
$('#payWithBitcoin').click(function() {
var email = $('#email').val();
if (${!userLoggedIn} && (!email || email.length == 0 || email.indexOf("@") == -1)) { // simple validation here; actual - on the server
alert("Please enter a valid email");
} else {
$.post("${root}/cart/bitcoinCheckout", {email: email}, function(data) {
$("#bitcoinPurchase").attr("data-code", data);
$(".coinbase-button").each(function (b, d) {
var f, g, h, i, j, k;
return f = $(d), h = f.data(), h.referrer = document.URL, j = $.param(h), g = f.data("code"), k = f.data("width") || 195, i = f.data("height") || 46, f.data("button-style") !== "none" && f.replaceWith("<iframe src='" + c + "/buttons/" + g + "?" + j + "' id='coinbase_button_iframe_" + g + "' name='coinbase_button_iframe_" + g + "' style='width: " + k + "px; height: " + i + "px; border: none; overflow: hidden;' scrolling='no' allowtransparency='true' frameborder='0'></iframe>"), $("body").append("<iframe src='https://coinbase.com/buttons/" + g + "/widget?" + j + "' id='coinbase_modal_iframe_" + g + "' name='coinbase_modal_iframe_" + g + "' style='background-color: transparent; border: 0px none transparent; overflow: hidden; display: none; position: fixed; visibility: visible; margin: 0px; padding: 0px; left: 0px; top: 0px; width: 100%; height: 100%; z-index: 9999;' scrolling='no' allowtransparency='true' frameborder='0'></iframe>");
});
$("#coinbase_modal_iframe_" + data).load(function() {
$(document).trigger('coinbase_show_modal', data);
});
});
}
});
$(document).on('coinbase_payment_complete', function(event, code){
$.post("${root}/cart/clear", function() { //clear the contents of the cart
window.location = "/?message=Checkout successful. Check your email.";
});
});
});
<a id="payWithBitcoin" href="javascript:void(0);"><img src="${staticRoot}/img/bitcoin.png"/></a>
<div class="coinbase-button" id="bitcoinPurchase" data-button-style="none"></div>
<script src="https://coinbase.com/assets/button.js" type="text/javascript"></script>
Another thing to have in mind is that there are no official clients – you need to invoke the RESTful API manually. This is simple, of course. You can use a RestTemplate and Jackson, for example.
public String getButtonCode(BigDecimal price, long purchaseId) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
ButtonRequest buttonRequest = new ButtonRequest(); //this is a value-object specifically defined for this request
buttonRequest.setCurrency("btc");
buttonRequest.setCustom(String.valueOf(purchaseId));
buttonRequest.setPrice(price.toPlainString());
buttonRequest.setType("buy_now");
buttonRequest.setName("Computer-generated tracks");
ResponseEntity<String> entity = template.postForEntity("https://coinbase.com/api/v1/buttons?api_key=" + coinbaseKey, buttonRequest, String.class);
String json = entity.getBody();
try {
JsonNode node = jsonMapper.readTree(json);
return node.get("button").get("code").asText();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
And finally, when the callback URL is invoked, you need to get the order id and finalize the payment:
@RequestMapping("/confirmBitcoinPurchase")
@ResponseBody
public void confirmBitcoin(@RequestBody String json) throws Exception {
JsonNode root = mapper.readTree(json);
JsonNode order = root.get("order");
if (order.get("status").asText().equals("completed")) {
String paymentId = order.get("id").asText();
.....
}
}
Overall, it takes some time to figure out. Previous experience with payment provider integration would be a plus, but there’s one important difference – you do not submit anything user-specific to the payment provider (like credit-card details). Instead, the user makes the payment to a target bitcoin address, which is handled by the payment provider. Whenever the transaction is complete, the provider invokes your URL.
Then you can get your money from the payment gateway either via a bank transfer, or by transferring them to your own bitcoin wallet.
Bitcoin is gaining popularity, and last week I decided to implement it as a payment option for my computer-generated music service. Why I decided to do that is detailed here (payment is only needed for commercial use of the music), but here I’ll share the technical details for implementing the bitcoin integration.
First, you need to pick a payment gateway/provider. You can pick one from this list. They differ in terms of their API and specific flow, but the general flow should be almost the same. Some of the implementation details below are relevant only to what I picked up – Coinbase – but they should be similar for other providers. I picked Coinbase, because it seemed the most developer-friendly and straightforward. It has one bug that I had to workaround, but apart from that it’s ok.
The general flow (probably containing some Coinbase specifics) is below. Note that I am describing the most complex scenario, where you have multiple items with variable prices and quantities. You can use the html/js code I used in my cart page. On the other hand, it’s assuming a best-case payment provider that does not redirect the user to external pages and uses simply a javascript dialog.
- Place a custom “Pay with Bitcoin” button on the checkout page
- Clicking it submits a form and thus triggers a back-end checkout process
- The back-end code saves a new purchase/order in the database and returns its id
- Then the back-end code invokes the payment gateway API, providing the order id (or any custom application data) and the price, and gets a code to identify the transaction
- The price can be provided in BTC or any currency. Depending on the API, you may want/need to convert a fixed price in a popular currency to BTC before passing it via the API. If you do so, be sure to cache the conversion rates for a while – no need to fetch them every time. Providers may do this conversion automatically, but if you want to display it to the user first, you may want to do the conversion yourself.
- On returning the code to the front-end, the javascript gets it, and opens a dialog (using a js function/event provided by the provider).
- The dialog may contain multiple payment options, but it usually shows a bitcoin payment address to which the user should send the money. He can do that with his mobile or desktop wallet
- After the transaction is completed, a callback URL is invoked on your application, which contains the order id that you passed when generating the code. Then you can confirm the purchase and send the items purchased.
- Then the dialog is automatically closed (or the user presses “transaction complete”). At that point you need to send an (ajax) request that clears the cart contents, and redirect to a “Thank you” page.
Note that the documentation of the payment provider should provide all the implementation details. Below I’ll share some specifics of my case: Coinbase and Java:
The default scenario described in the docs is to put a button with a predefined code on the page. That doesn’t work if you need to increase quantities or have multiple items. That’s why you should dynamically generate the button code. But the javascript library only handles this on page load. So I had to copy some minified javascript code and invoke it when the code is returned from the back-end. Here is the whole javascript code (invoked when the user presses “Pay with bitcoin”):
$(document).ready(function() { $('#payWithBitcoin').click(function() { var email = $('#email').val(); if (${!userLoggedIn} && (!email || email.length == 0 || email.indexOf("@") == -1)) { // simple validation here; actual - on the server alert("Please enter a valid email"); } else { $.post("${root}/cart/bitcoinCheckout", {email: email}, function(data) { $("#bitcoinPurchase").attr("data-code", data); $(".coinbase-button").each(function (b, d) { var f, g, h, i, j, k; return f = $(d), h = f.data(), h.referrer = document.URL, j = $.param(h), g = f.data("code"), k = f.data("width") || 195, i = f.data("height") || 46, f.data("button-style") !== "none" && f.replaceWith("<iframe src='" + c + "/buttons/" + g + "?" + j + "' id='coinbase_button_iframe_" + g + "' name='coinbase_button_iframe_" + g + "' style='width: " + k + "px; height: " + i + "px; border: none; overflow: hidden;' scrolling='no' allowtransparency='true' frameborder='0'></iframe>"), $("body").append("<iframe src='https://coinbase.com/buttons/" + g + "/widget?" + j + "' id='coinbase_modal_iframe_" + g + "' name='coinbase_modal_iframe_" + g + "' style='background-color: transparent; border: 0px none transparent; overflow: hidden; display: none; position: fixed; visibility: visible; margin: 0px; padding: 0px; left: 0px; top: 0px; width: 100%; height: 100%; z-index: 9999;' scrolling='no' allowtransparency='true' frameborder='0'></iframe>"); }); $("#coinbase_modal_iframe_" + data).load(function() { $(document).trigger('coinbase_show_modal', data); }); }); } }); $(document).on('coinbase_payment_complete', function(event, code){ $.post("${root}/cart/clear", function() { //clear the contents of the cart window.location = "/?message=Checkout successful. Check your email."; }); }); });
<a id="payWithBitcoin" href="javascript:void(0);"><img src="${staticRoot}/img/bitcoin.png"/></a> <div class="coinbase-button" id="bitcoinPurchase" data-button-style="none"></div> <script src="https://coinbase.com/assets/button.js" type="text/javascript"></script>
Another thing to have in mind is that there are no official clients – you need to invoke the RESTful API manually. This is simple, of course. You can use a RestTemplate and Jackson, for example.
public String getButtonCode(BigDecimal price, long purchaseId) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); ButtonRequest buttonRequest = new ButtonRequest(); //this is a value-object specifically defined for this request buttonRequest.setCurrency("btc"); buttonRequest.setCustom(String.valueOf(purchaseId)); buttonRequest.setPrice(price.toPlainString()); buttonRequest.setType("buy_now"); buttonRequest.setName("Computer-generated tracks"); ResponseEntity<String> entity = template.postForEntity("https://coinbase.com/api/v1/buttons?api_key=" + coinbaseKey, buttonRequest, String.class); String json = entity.getBody(); try { JsonNode node = jsonMapper.readTree(json); return node.get("button").get("code").asText(); } catch (IOException e) { throw new IllegalStateException(e); } }
And finally, when the callback URL is invoked, you need to get the order id and finalize the payment:
@RequestMapping("/confirmBitcoinPurchase") @ResponseBody public void confirmBitcoin(@RequestBody String json) throws Exception { JsonNode root = mapper.readTree(json); JsonNode order = root.get("order"); if (order.get("status").asText().equals("completed")) { String paymentId = order.get("id").asText(); ..... } }
Overall, it takes some time to figure out. Previous experience with payment provider integration would be a plus, but there’s one important difference – you do not submit anything user-specific to the payment provider (like credit-card details). Instead, the user makes the payment to a target bitcoin address, which is handled by the payment provider. Whenever the transaction is complete, the provider invokes your URL.
Then you can get your money from the payment gateway either via a bank transfer, or by transferring them to your own bitcoin wallet.
Hi,
I’m not that familiar with how BitCoin payment works, but why does one need a BitCoin payment provider?
Couldn’t you just create a BitCoin address for each purchase yourself and check on your server if the payment was recieved?
Technically, yes. But you lose the automated verification – i.e. if you need to send the user something as soon as the payment is received, you’d have to do some low-level stuff in order to get that automated.
I’ve got the button working, BTC is passing, but I’ve got no idea what to put into the callback URL. I’m on a Windows hosted web server, so I can use ASP but also of course javascript and JSON. Coinbase want’s to post? Is there some place I can find an example? If I can just get the call back working. I have two accounts at coinbase so I can test sending to myself. Just no idea how to begin writing this callback page. Thank you.
Hi,
I am new to Bitcoin payment.The information shared in this article about how to accept bitcoin payment is very nice and helpful.Thanks for sharing.
No need to use the service of a bitcoin company provider. Just visit this page and generate bitcoin payment buttons for free: https://accept-bitcoin.net/bitcoin-payment-button.php