Introduction

A webhook is a URL on your server where we send a JSON payload over HTTP as such events occur. Webhooks are important in integrating FlashPay, they allow FlashPay notify you once an event occurs. For example, when you set webhook URL on your FlashPay dashboard, once a payment transaction is successful, we will immediately notify your server.

When your webhook URL receives an event, it needs to acknowledge the event with an HTTP response. The request times out if a response is not received in 10 seconds, acknowledging an event means returning a 200 OK HTTP response. Without a 200 OK response, we will keep sending events every 30 minutes for a maximum of five(5) times.


Webhook Payload Structure

Webhook events follow the same structure:

fielddescription
txn_referenceUnique transaction reference of the successful transaction.
assetAsset ID.
senderAlgorand address of the sender.
recipientAlgorand address of the recipient.
txn_typeTransaction type (normal, payment_link)
txn_hashHash of the transaction on the Algorand blockchain.
amountThe total amount paud.
statusTransaction status (usually success because we only send webhook notifications when a transaction is successful
networkTransaction network (testnet, mainnet)


Here is sample webhook payloads for successful payment:

{
  "txn_reference": "fp_399c37cbd2824aed891738a033a1ad5b_03ef72",
  "asset": 10458941,
  "sender": "XQ52337XYJMFNUM73IC5KSLG6UXYKMK3H36LW6RI2DRBSGIJRQBI6X6OYI",
  "txn_type": "payment_link",
  "recipient": "J7ZIYHAHBSNHO5SDR44WY3R4GKSBA6DWJGNUNYB2F3SNMEU2WAVY6OTFNQ",
  "txn_hash": "F23RSTSTWEWMX3LWZ3ZEUHRWFPOIXXAPOWS2DJ7YHK5NC3VKKTDA",
  "amount": "10.0000",
  "status": "success",
  "created_at": "2022-10-02T22:26:35.843598Z",
  "updated_at": "2022-10-02T22:26:35.843618Z",
  "network": "testnet"
}


Verifying Webhook Origin

Since your webhook URL is publicly accessible and anyone can send a fake payload, you need to verify that events sent to your webhook URL are from FlashPay.

Webhook events from FlashPay have an x-flashpay-signature header. The value of this header is a HMAC 512 hash of the webhook payload signed with your secret key. Check if the header is present in the request headers and verify the value before processing the event.

var crypto = require('crypto');
var secret = process.env.SECRET_KEY;

// Using Express
app.post("/my/webhook/url", function(req, res) {
    // Verify event is from Flashpay
    const hash = crypto.createHmac('sha512', secret).update(JSON.stringify(req.body)).digest('hex');
    if (hash == req.headers['x-flashpay-signature']) {
    // Retrieve the request's body
    const payload = req.body;
    // Do something with event
    }
    res.send(200);
});
import hmac
import hashlib
import os
import request

secret = os.environ['SECRET_KEY']

// Using Flask
@app.route("/my/webhook/url", methods=['POST'])
def my_webhook_url():
    # Verify event is from Flashpay
    hash = hmac.new(secret, msg=request.json, digestmod=hashlib.sha512).hexdigest()
    if hash == request.headers['x-flashpay-signature']:
        # Retrieve the request's body
        payload = request.json
        # Do something with event
    return ('', 200)
$secret = getenv('SECRET_KEY');

// Using Express
$app->post("/my/webhook/url", function($req, $res) {
    // Verify event is from Flashpay
    $hash = hash_hmac('sha512', json_encode($req->body), $secret);
    if ($hash == $req->headers['x-flashpay-signature']) {
    // Retrieve the request's body
    $payload = $req->body;
    // Do something with event
    }
    $res->send(200);
});
String secret = System.getenv("SECRET_KEY");

// Using Spring (MVC)
@PostMapping("/my/webhook/url")
public ResponseEntity<String> post(@RequestBody String payload, @RequestHeader("X-Flashpay-Signature") String hash) {
    // Verify event is from Flashpay
    String hash = HmacUtils.hmacSha512Hex(secret, payload);
    if (hash == req.headers['x-flashpay-signature']) {
    // Retrieve the request's body
    // Do something with event
    }
    return ResponseEntity.ok().build();
}
func main() {
	// Verify event is from Flashpay
	hash := crypto.Hmac512(os.Getenv("SECRET_KEY"), []byte(req.Body))
	if hash == req.Header("X-Flashpay-Signature") {
		// Retrieve the request's body
		payload := req.Body
		// Do something with event
	}
	res.Send(200)
}