Software Development

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.

  1. Place a custom “Pay with Bitcoin” button on the checkout page
  2. Clicking it submits a form and thus triggers a back-end checkout process
  3. The back-end code saves a new purchase/order in the database and returns its id
  4. 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
  5. 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.
  6. 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).
  7. 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
  8. 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.
  9. 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.
 

Reference: How To Accept Bitcoin Payments from our JCG partner Bozhidar Bozhanov at the Bozho’s tech blog blog.

Bozhidar Bozhanov

Senior Java developer, one of the top stackoverflow users, fluent with Java and Java technology stacks - Spring, JPA, JavaEE, as well as Android, Scala and any framework you throw at him. creator of Computoser - an algorithmic music composer. Worked on telecom projects, e-government and large-scale online recruitment and navigation platforms.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Steve
Steve
2 years ago

Oh, that’s interesting, but I think working with cryptocurrencies is way too complicated. I run a business, and I have to work with three currencies, and I think it’s pretty hard already. Well, I just opened three IBANs in 1 wallet on this page https://genome.eu/high-risk-merchant-account-pricing/, and now it’s not as hard as it was because I managed to do that using one wallet.

Jijnya
Jijnya
2 years ago

Oh, that’s interesting, but I think working with cryptocurrencies is way too complicated. I run a business, and I have to work with three currencies, and I think it’s pretty hard already. Well, I just opened three IBANs in 1 wallet on this page https://genome.eu/high-risk-merchant-account-pricing/, and now it’s not as hard as it was because I managed to do that using one wallet.

Back to top button