Skip to content

Webhooks

Handling webhook events across all SDKs.

Overview

Webhooks deliver real-time events to your application.

TypeScript/JavaScript

Express.js

typescript
import express from "express";
import { IslamicOpenFinance } from "@iof/sdk";

const app = express();
const iof = new IslamicOpenFinance({ apiKey: "..." });

app.post(
  "/webhooks",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const signature = req.headers["x-iof-signature"] as string;

    try {
      const event = iof.webhooks.constructEvent(
        req.body,
        signature,
        process.env.WEBHOOK_SECRET!,
      );

      switch (event.type) {
        case "contract.created":
          await handleContractCreated(event.data.object);
          break;
        case "kyc.verified":
          await handleKycVerified(event.data.object);
          break;
      }

      res.status(200).send("OK");
    } catch (err) {
      console.error("Webhook error:", err);
      res.status(400).send("Invalid signature");
    }
  },
);

Next.js

typescript
// app/api/webhooks/route.ts
import { NextRequest, NextResponse } from "next/server";
import { IslamicOpenFinance } from "@iof/sdk";

const iof = new IslamicOpenFinance({ apiKey: "..." });

export async function POST(req: NextRequest) {
  const body = await req.text();
  const signature = req.headers.get("x-iof-signature")!;

  try {
    const event = iof.webhooks.constructEvent(
      body,
      signature,
      process.env.WEBHOOK_SECRET!,
    );

    // Handle event
    console.log("Received:", event.type);

    return NextResponse.json({ received: true });
  } catch (err) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 400 });
  }
}

Python

Flask

python
from flask import Flask, request
from iof import IslamicOpenFinance, WebhookSignatureError

app = Flask(__name__)
iof = IslamicOpenFinance(api_key="...")

@app.route("/webhooks", methods=["POST"])
def webhooks():
    payload = request.get_data()
    signature = request.headers.get("X-IOF-Signature")

    try:
        event = iof.webhooks.construct_event(
            payload,
            signature,
            os.environ["WEBHOOK_SECRET"]
        )

        if event.type == "contract.created":
            handle_contract_created(event.data.object)

        return "OK", 200
    except WebhookSignatureError:
        return "Invalid signature", 400

FastAPI

python
from fastapi import FastAPI, Request, HTTPException
from iof import IslamicOpenFinance, WebhookSignatureError

app = FastAPI()
iof = IslamicOpenFinance(api_key="...")

@app.post("/webhooks")
async def webhooks(request: Request):
    payload = await request.body()
    signature = request.headers.get("X-IOF-Signature")

    try:
        event = iof.webhooks.construct_event(
            payload,
            signature,
            os.environ["WEBHOOK_SECRET"]
        )

        # Handle event
        return {"received": True}
    except WebhookSignatureError:
        raise HTTPException(status_code=400, detail="Invalid signature")

Go

go
import (
    "github.com/Islamic-Open-Finance/iof-go/webhook"
)

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    payload, _ := io.ReadAll(r.Body)
    signature := r.Header.Get("X-IOF-Signature")

    event, err := webhook.ConstructEvent(
        payload,
        signature,
        os.Getenv("WEBHOOK_SECRET"),
    )
    if err != nil {
        http.Error(w, "Invalid signature", http.StatusBadRequest)
        return
    }

    switch event.Type {
    case "contract.created":
        var contract iof.Contract
        json.Unmarshal(event.Data.Raw, &contract)
        handleContractCreated(&contract)
    }

    w.WriteHeader(http.StatusOK)
}

Java

java
@PostMapping("/webhooks")
public ResponseEntity<String> handleWebhook(
    @RequestBody String payload,
    @RequestHeader("X-IOF-Signature") String signature
) {
    try {
        Event event = Webhook.constructEvent(
            payload,
            signature,
            System.getenv("WEBHOOK_SECRET")
        );

        switch (event.getType()) {
            case "contract.created":
                handleContractCreated((Contract) event.getData().getObject());
                break;
        }

        return ResponseEntity.ok("OK");
    } catch (SignatureVerificationException e) {
        return ResponseEntity.badRequest().body("Invalid signature");
    }
}

Best Practices

  1. Always verify signatures
  2. Return 200 quickly - Process async if needed
  3. Implement idempotency - Handle duplicate events
  4. Log event IDs - For debugging
  5. Use HTTPS - Secure endpoints only

Next Steps

Licensed under the Apache License 2.0