In this tutorial, we will understand how to integrate blockonomics payments API in a PHP-based shopping cart.
Resources
Source Code | https://github.com/blockonomics/Demo-PHP-Application |
Youtube Video | https://www.youtube.com/watch?v=dibw1sGz3to |
Tech Stack
- PHP
- HTML/CSS
- Javascript
Setting Up
Complete the setup using the instructions provided here.
The Logic
The Database
The Database is going to have four different Tables namely invoices
, orderes
, payments
and products
. Each table plays a crucial role in the application.
The invoice table stores all the invoices. It has an id
, code
, address
, price
which is the USD price, status
, product
and ip
columns. The IP column can help us to identify which invoices belong to the current user. The code is essentially the unique identifier for each invoice.
The payments table stores information about all the payments. It keeps track of the transaction id (txid), bitcoin address, value in satoshi and the status of the payment.
Understanding the Code
The Project Code hierarchy is as follows –
- check
- css
buy.php
config.php
functions.php
getprice.php
index.php
invoice.php
orders.php
- The
check
folder is used to store the callback from the Blockonomics. - The
css
folder contains the styles. buy.php
is the file we use to redirect the user to invoice page. It is responsible for creating the invoice.config.php
file stores the Blockonomics configuration. You can store all API’s and other key-value pairs here.functions.php
contains bunch of different functions that we use inside our application.getprice.php
simply returns the btc price for given USD.index.php
which is the homepage and contains all products.invoice.php
which is the invoice page and contains the blockonomics websocket.orders.php
which displays the orders of the user.
Currency Conversion
Inside getprice.php
we get the current invoice price which is in USD and convert it to BTC.
<?php
include_once "functions.php";
$invoice = $_REQUEST['code'];
$price = getInvoicePrice($invoice);
$price = USDtoBTC($price);
echo $price*100000000;
?>
The functions getInvoicePrice
and USDtoBTC
are present inside the functions.php
which is included at the very top in code.
New Address Generation
To generate a new bitcoin address, you have to use Blockonomics API which is documented here. We can see the function generateAddress
which uses this API to generate new address.
function generateAddress(){
global $apikey;
global $url;
$options = array(
'http' => array(
'header' => 'Authorization: Bearer '.$apikey,
'method' => 'POST',
'content' => '',
'ignore_errors' => true
)
);
$context = stream_context_create($options);
$contents = file_get_contents($url."new_address", false, $context);
$object = json_decode($contents);
// Check if address was generated successfully
if (isset($object->address)) {
$address = $object->address;
} else {
// Show any possible errors
$address = $http_response_header[0]."\n".$contents;
}
return $address;
}
HTTP CallBack Request
Blockonomics uses the HTTP CallBack URL
provided by you to send notifications about the payment status updates. The documentation provides a brief description about parameters of this request. We have implemented the callback endpoint inside the index.php
file present in check
folder.
<?php
/*
Callback location, set this in blockonmics merchant page
For testing payments locally, use this:
localhost/bitcoin/check?secret=asecretcode&addr=[ADDRESS]&status=[STATUS CODE]&txid=[TXID]&value=[Amount paid in satoshi]
*/
include_once "../config.php";
include_once "../functions.php";
$secretlocal = "asecretcode"; // Code in the callback, make sure this matches to what youve set
// Get all these values
$status = 0;
$txid = $_GET['txid'];
$value = $_GET['value'];
$status = $_GET['status'];
$addr = $_GET['addr'];
$secret = $_GET['secret'];
// Check all are set
if(empty($txid) || empty($value) || empty($addr) || empty($secret)){
exit();
}
if($secret != $secretlocal){
exit();
}
$sql = "INSERT INTO `payments` (`txid`, `value`, `addr`, `status`)
VALUES ('$txid', '$value', '$addr', '$status')";
mysqli_query($conn, $sql);
echo $sql;
// Get invoice price
$code = getCode($addr);
$price = getInvoicePrice($code);
// Convert price to satoshi for check
$price = USDtoBTC($price);
$price = $price *100000000;
// Expired
if($status < 0){
exit();
}
if($value >= round($price)){
// Update invoice status
updateInvoiceStatus($code, $status);
if($status == 2){
// Correct amount paid and fully confirmed
// Do whatever you want here once payment is correct
$invoice = getInvoice($addr);
createOrder($invoice, getInvoiceIp($addr));
}
}else {
// Buyer hasnt paid enough
updateInvoiceStatus($code, -2);
}
?>
Inside this endpoint, we should update our database with the new status of the given payment. The updateInvoiceStatus
function does this job and it takes two parameters. The code
which is the unique identifier of each payment and second is the status value. To find the code, we use address
because each payment has or should have a unique address
. It is strongly advised to NOT reuse your bitcoin address.
Blockonomics WebSocket
Blockonomics provide websockets, so your application (client) can get instant update on the status of any transaction and provide relevant view of the application as a next step. The websocket is implemented on the invoice.php
page because the user will perform the transaction on this page and thus, we would like to know when the user does the transaction. Upon receiving status update from the websocket, we can conclude that user has completed the transaction. This does not guarantee that you have received your payment. Please use HTTP CallBack URL endpoint to update the staus of the transaction and from this checkout page, DO NOT show that your payment is successful, until it is confirmed by the CallBack Endpoint.
<script>
var status = <?php echo $statusval; ?>
// Create socket variables
if(status < 2 && status != -2){
var addr = document.getElementById("address").innerHTML;
var wsuri2 = "wss://www.blockonomics.co/payment/"+ addr;
// Create socket and monitor
var socket = new WebSocket(wsuri2, "protocolOne")
socket.onmessage = function(event){
console.log(event.data);
response = JSON.parse(event.data);
//Refresh page if payment moved up one status
if (response.status > status)
setTimeout(function(){ window.location=window.location }, 1000);
}
}
</script>