OpenNode makes it easy for developers to accept Bitcoin payments. However, I couldn't find any PHP code samples on how to capture each payment information. In this guide, you will see how I was able to securely capture each charge via webhook.
This post assumes you already have a OpenNode account and you can successfully create a bitcoin charge.
creating a charge with callback
I created a charge using the official PHP SDK. And to receive a webhook notification, I provided a callback_url
:
$charge_params = array(
"amount" => 50.00,
"currency" => "USD",
"callback_url" => "https://webhook.site/b07fsfwrw"
);
By providing a callback_url
, OpenNode sends a POST request to my callback_url. Each request sent contains a payload with the following information:
POST callback_url | application/x-www-form-urlencoded
{
id: id,
callback_url: callback_url,
success_url: success_url,
status: status,
order_id: order_id,
description: description,
price: price,
fee: fee,
auto_settle: auto_settle,
hashed_order: hashed_order
}
capturing the event payload
To capture this payload, I created the callback route on my application with the following code:
if ( $json = json_decode( file_get_contents("php://input"), true ) ) {
$charge = $json;
} else {
$charge = $_POST;
}
if ( empty($charge) || !is_array($charge) ) {
exit("charge error");
}
$apiKey = "OPENNODE-TOKEN";
$recieved = $charge["hashed_order"];
$calculated = hash_hmac( "sha256", $charge["id"], $apiKey );
if ( !hash_equals( $calculated, $recieved) ) {
exit("failed");
}
print_r( $charge ); // print the charge data
This successfully captures the webhook for each charge on OpenNode.
json or form-data
You may notice on the first line I try to capture the charge as a JSON payload but OpenNode always sends webhook data as form-urlencoded. Don't mind, it is just my "bullet-proof" way of dealing with any webhook and the majority of webhook data is in JSON.
However, if you mind, rather than this:
if ( $json = json_decode( file_get_contents("php://input"), true ) ) {
$charge = $json;
} else {
$charge = $_POST;
}
you remove the if statements:
$charge = $_POST;
works 100% with OpenNode.
block unknown request
To avoid touching stories, you have to validate the webhook requests.
OpenNode signs all charge related events it sends to your callback URL with a hashed_order
field on the event payload. This allows you to validate that the events were sent by OpenNode and not by a third party.
To validate, you verify the signatures by computing an HMAC with the SHA256 hash function. This is very easy in PHP. Just use the hash_hmac
function:
$apiKey = "YOUR OPENNODE API KEY";
$recieved = $charge["hashed_order"];
$calculated = hash_hmac("sha256", $charge["id"], $apiKey);
The hash_hmac function requires three parameters; the hash algorithm, the data or message, and the key.
- the hash algorithm is sha256
- the hash data or message is the
hashed_order
field in the webhook payload - the key is your OpenNode api key.
We then check if the calculated hash matches the hashed_order in the event payload.
if ( !hash_equals( $calculated, $recieved) ) {
exit("failed");
}
This way we ensure no third party request will pass through successfully.
You can then proceed with whatever you intend to use the event payload for knowing it coming from OpenNode. You can read the official webhook documentation.
I hope this help.