How to Accept Bitcoin Cash in Your Own Web Store Using BIP70 Payment Requests

Setting up Electron Cash with Support for Payment Requests and Websockets *NB: PR = Payment Request and EC = Electron Cash BIP70 uses SSL certificates to securely transmit payment requests from merchant to customer. Although the spec itself does not place any restrictions on what SSL certs are valid, wallets that use bitcore payment protocol, for example, do. For this reason, make sure that your SSL root certificate is shown in this list. This method uses a tweaked and improved version of electrum-merchant to embed BCH PRs as a widget (code shown below). In the following guide, EC will expect you to have a file called 'index.html' in your PR directory (a relic of electrum-merchant), otherwise it will not start. Instead of installing the outdated electrum-merchant, create an empty file with this name in your requests_dir to get EC up and running. In the meantime I will see if I can get this needless restriction removed from EC. The latest guide can be found here.

Creating the Request and Configuring Index URL Index URL is the page on your site that will display the PR to the customer. However, your webserver will need to create the request before it does so. The exact way to do this will depend on how your website is implemented. Here is an example of how I do it in php by accessing EC's JSON-RPC server: $response = $client->request('POST', 'http://[YOUR_EC_RPC_USER]:[YOUR_EC_RPC_PW]@127.0.0.1:[YOUR_EC_RPC_PORT]', ['json' => [

'id' => 'curltext',

'method' => 'addrequest',

'params' => [

(($payment->getAmount() / 100) / $BCHUSD),

'BCH Coffee Shop order #' . $ordernum,

$exp ]]]);

$answer = json_decode((string)$response->getBody(), true);

$json = $answer['result']; Now that the PR is created with some 10-character request ID, e.g. '3741630f94', you will have created a new directory such as: mywebroot/myrequests_dir/req/3/7/3741630f94/ This directory will contain 2 files: A JSON file, e.g. 3741660f84.json , containing the PR data defined in EC and a google protocol buffers PR file, 3741660f84 , which is defined in the BIP70 spec and will be sent to the customer's wallet when they are ready to pay. *NB: While electrum-merchant simply used the index_url provided in this JSON file to display each PR with a GET request, we will do it differently so we can embed the PR in any URL we choose by extracting the 'id' instead. The PR JSON can be extracted from $output to obtain the 'id' and (optionally) persist data to our database. An example of how I do it using Symfony: //Retrieve the payment request data and create a record in db

$payment_json = "";

foreach ($op as $line) {

$payment_json .= $line;

}

$json = json_decode($payment_json, true);

$address = $json['address'];

$id = $json['id'];

$satoshis = $json['amount'];

$created = new \DateTime;

$created->setTimestamp($json['time']);

$bchpayment = New BitcoinCashPayment();

$bchpayment->setOrderId($order->getId());

$bchpayment->setAddress($address);

$bchpayment->setOrdernum($ordernum);

$bchpayment->setDeadline($deadline);

$bchpayment->setRequestid($id);

$bchpayment->setSatoshis($satoshis);

$bchpayment->setCreatedAt($created);

$em->persist($bchpayment-);

$em->flush();

...

Now that we have our PR ready and we also have our 'id', we can display the PR in a widget. The following is a simplified example of a widget with accompanying javascript/jquery modified from electrum-merchant. You can include it in your site, e.g. in a layout, after generating it with the necessary PHP variables. The styling is done with semantic ui but you can replace the html tags with whatever you want. Variables that cannot be hardcoded are shown as $var, those that can are shown in bold: <link rel="stylesheet" type="text/css" href="path/to/your/jquery-ui.min.css"> <script src="path/to/your/jquery.js"></script> <script src="path/to/your/jquery-ui.js"></script> <script src="path/to/your/qrcode.js (from https://davidshimjs.github.io/qrcodejs/)"></script> <script type="text/javascript"> var current; var max = 100; var initial = $PR-JSON-'time'-field; var duration = $PR-JSON-'exp'-field; var redirecting = false; $(function () { if(duration){ var current = 100 * (Math.floor(Date.now()/1000) - initial)/duration; $("#progressbar").progressbar({ value: current, max: max }); function update() { if (redirecting) return; current = 100 * (Math.floor(Date.now()/1000) - initial)/duration; $("#progressbar").progressbar({ value: current }); remaining = duration - Math.floor(Date.now()/1000 - initial); minutes = Math.floor(remaining / 60); seconds = remaining % 60; if(seconds < 10) seconds = "0" + seconds; $("#remaining").text(minutes + ":" + seconds); if (current >= max) { $("#container").html("This invoice has expired. You will be redirected shortly..."); window.location = "https://yourdomain.com/your-PR-expiration-redirect-page"; redirecting = true; } }; var interval = setInterval(update, 1000); } }); new QRCode(document.getElementById("qrcode"), $PR-JSON-'URI'-field); var wss_server = "YOUR_EC_WEBSOCKET_SERVER_CONFIG"; var wss_port = "YOUR_EC_WEBSOCKET_PORT_CONFIG"; var wss_address = "wss://" + wss_server + ":" + wss_port +"/"; console.log("Opening WSS: " + wss_address) var ws = new WebSocket(wss_address); ws.onopen = function() { ws.send('id:' + "$PR-JSON-'id'-field"); }; ws.onmessage = function (evt) { var received_msg = evt.data; if(received_msg.length == 4 && received_msg == 'paid') { $("#container").html("This invoice has been paid. You will be redirected shortly..."); window.location = "https://yourdomain.com/thank-you-page"; } else alert("Message received: "+ received_msg); }; $(document).on('click', 'a[href^="bitcoincash:"]', function (e) { e.preventDefault(); var btcWindow = window.open($(e.currentTarget).attr('href')); btcWindow.close(); return false; }); <style> #container { padding-top: 40px; } .ui-progressbar { position: relative; } #remaining { position: absolute; left: 50%; margin-left: -15px; top: 6px; font-weight: bold; text-shadow: 1px 1px 0 #fff; } </style> <div class="ui center aligned segment" id="container" style="max-width: 400px; min-height: 400px; text-align:center; margin:auto; font-family:arial, serif;"> <h1 id="reason"><b>$PR-JSON-'memo'-field</b></h1> <p id="error"></p> <h1 id="amount" style="font-weight: bold;">($PR-JSON-'amount'-field / 100000000) formatted to 8 decimal places) BCH</h1> <a style="color:#ffffff; text-decoration:none; display: inline-block; width: 100%; height: 100%;" id="paylink" href="$PR-JSON-'URI'-field"> <div style="background-color:#4cca47; border-radius: 5px; padding:10px;"> Pay in Wallet </div> </a> <br/> <div id="qrcode" align="center"></div> <p id="powered" style="font-size:80%;">Powered by Electron Cash</p> <div id="progressbar"> <div id="remaining"></div> </div> </div> Phew! Now you can configure your web server to send your customer to a URL such as https://yourdomain.com/bch-request/3741630f94 where the above page + widget will be generated according to the PR id '3741630f94 ' and displayed to them. When they click 'Pay in Wallet' or scan the QR code, they will receive the PR file directly in their wallet. But first we need to configure request_url...

Configuring Request URL When you set up EC, you configured a request_url which has a unique PR id appended onto it for every PR that is generated. Bitcoin Cash wallets that support BIP70 will visit this URL with a GET request when the customer clicks 'Pay in Wallet' or scans the QR code. Some wallets (like Bitcoin.com) will send a CORS preflight OPTIONS request beforehand. Either way, the response to this URL needs to have the necessary CORS headers configured in addition to providing the binary PR file located at your_requests_dir/req/3/7/3741630f94/3741630f94 (as an example). The required resonse headers are as follows: access-control-allow-origin: * access-control-allow-methods: POST, GET, HEAD access-control-allow-headers: Content-Type, Origin, x-requested-with Content-Type: application/bitcoincash-paymentrequest

Configuring Payment URL BIP70 technically only requires that the customer broadcast the payment as specified in the PR without having to respond to the merchant directly with a Payment file. A lot of people disagree with this, including many wallet devlopers. Given that: The Payment file is useful for communicating with the customer regarding the status of the payment via a PaymentACK You probably want to maximize compatibility with as many wallets as possible EC creates a PR file with payment_url specified by default ...you're going to have to support it on your site. To make things more complicated, not all BCH wallets will be satisfied with the same kind of response. The Payment file is always sent with a POST request, but some wallets will set the Content-Type header to 'application/bitcoincash-payment' and expect a standard BIP70 response. Others (Bitcoin.com) will set the Content-Type header to 'application/payment', switching over from BIP70 to Bitpay's JSON 2.0 Payment Protocol half way through the process. Yikes! Your site needs to identify which kind of request it is receiving by examining this header and produce the correct response. The Content-Type response header for requests of type application/bitcoincash-payment (BIP70) need to be set to 'application/bitcoincash-paymentack' The Content-Type response header for requests of type application/payment (JSON 2.0) need to be set to 'application/payment-ack' In order to generate the PaymentACK for the response, we need to use google's protocol buffers. You can create your own or you can compile my own C++ tools from here. Here's an example showing how I do it in PHP: exec("getack " . $tmp_directory . "payment \"Transaction received by BCH Coffee Shop. Invoice will be marked as paid if the transaction is confirmed.\" " . $tmp_directory . "paymentack", $output_txns); getack will both create a PaymentACK file to be sent back to the wallet as a binary file as well as output an array of raw BitcoinCash txns to $output_txns. Technically, you should only broadcast these transactions after examining them with a tool such as bitcoin ABC's bitcoin-tx to make sure that the they are sufficient to satisfy the payment request. I'm not going to go into detail about how to do this but, assuming that you have done so, or wish not to do so, you should now broadcast these transactions (even though the customer may also already have done so): electron-cash broadcast [TXN HEX] If the Payment file came from a wallet that expects a JSON response then respond with JSON instead of sending the binary PaymentACK file. The format of that response is described here. Remember, even though the content is JSON, the Content-Type header of the response must be 'application/payment-ack' This kind of response will also require the response header 'access-control-allow-origin' to be set to '*'. Congratulations! Your site is now configured to accept Bitcoin Cash payments with BIP70!