> ## Documentation Index
> Fetch the complete documentation index at: https://beta-developers.mizaniyapay.dz/llms.txt
> Use this file to discover all available pages before exploring further.

# Complete VTPE Integration Example

> End-to-end VTPE integration code showing the Product Information API, webhook handler with signature verification, and all three event types.

This page provides a complete, working integration example that combines all VTPE concepts into a single server. It includes the Product Information API endpoint, a webhook handler with HMAC signature verification, and logic for all three event types.

## Project Setup

Set the following environment variables before starting your server:

```bash theme={null}
VTPE_API_SECRET=your_api_secret
VTPE_HMAC_SECRET=your_hmac_secret
```

## Complete Integration

<CodeGroup>
  ```javascript Node.js theme={null}
  import express from "express";
  import crypto from "crypto";

  const app = express();

  // Middleware to capture raw body for HMAC verification
  app.use(express.json({
    verify: (req, res, buf) => {
      req.rawBody = buf;
    }
  }));

  // Auth middleware
  function authMiddleware(req, res, next) {
    const auth = req.headers["authorization"];
    if (auth !== `Bearer ${process.env.VTPE_API_SECRET}`) {
      return res.status(401).json({ error: "UNAUTHORIZED", errorMessage: "Invalid or missing API Secret" });
    }
    next();
  }

  // HMAC signature verification middleware
  function verifySignature(req, res, next) {
    const timestamp = req.headers["x-timestamp"];
    const signature = req.headers["x-signature"];

    if (!timestamp || !signature) {
      return res.status(401).json({ error: "UNAUTHORIZED", errorMessage: "Missing signature headers" });
    }

    // Validate timestamp window (5 minutes)
    const now = Math.floor(Date.now() / 1000);
    const ts = parseInt(timestamp, 10);
    if (Math.abs(now - ts) > 300) {
      return res.status(401).json({ error: "UNAUTHORIZED", errorMessage: "Timestamp outside acceptable window" });
    }

    const payload = req.rawBody.toString("utf8") + timestamp;
    const expected = crypto
      .createHmac("sha256", process.env.VTPE_HMAC_SECRET)
      .update(payload)
      .digest("hex");

    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
      return res.status(401).json({ error: "UNAUTHORIZED", errorMessage: "Invalid signature" });
    }

    next();
  }

  // Product Information API
  app.get("/payments/:reference", authMiddleware, (req, res) => {
    const { reference } = req.params;
    const order = db.findOrder(reference);

    if (!order) {
      return res.status(404).json({ error: "NOT_FOUND", errorMessage: "Reference not found" });
    }
    if (order.isPaid) {
      return res.status(409).json({ error: "ALREADY_PAID", errorMessage: "Payment already completed" });
    }

    return res.json({
      reference: order.reference,
      total_amount: order.totalAmount,
      currency: order.currency,
      amount_detail: order.amountDetail ?? [],
      details: order.details ?? []
    });
  });

  // Webhook handler
  app.post("/webhooks/vtpe", authMiddleware, verifySignature, async (req, res) => {
    const { event, data } = req.body;

    if (event === "payment.initialized") {
      await db.createPendingPayment({
        paymentId: data.paymentId,
        reference: data.reference,
        amount: data.amount,
        currency: data.currency,
        status: "pending"
      });
    }

    if (event === "payment.success") {
      const alreadyProcessed = await db.getPaymentByPaymentId(data.paymentId);
      if (alreadyProcessed) {
        return res.status(200).json({ success: true });
      }

      await db.fulfillOrder({
        reference: data.reference,
        paymentId: data.paymentId,
        paidAt: data.paidAt,
        channel: data.channel
      });
    }

    if (event === "payment.fail") {
      await db.cancelOrder({
        reference: data.reference,
        paymentId: data.paymentId
      });
    }

    return res.status(200).json({ success: true });
  });

  app.listen(3000, () => {
    console.log("VTPE integration server running on port 3000");
  });
  ```

  ```python Python theme={null}
  from flask import Flask, request, jsonify
  import os
  import hmac
  import hashlib
  import time

  app = Flask(__name__)

  VTPE_API_SECRET = os.environ["VTPE_API_SECRET"]
  VTPE_HMAC_SECRET = os.environ["VTPE_HMAC_SECRET"]

  # Auth middleware
  @app.before_request
  def check_auth():
      if request.endpoint in ("get_payment", "handle_webhook"):
          auth = request.headers.get("Authorization", "")
          if auth != f"Bearer {VTPE_API_SECRET}":
              return jsonify({"error": "UNAUTHORIZED", "errorMessage": "Invalid or missing API Secret"}), 401

  # HMAC signature verification
  def verify_signature():
      timestamp = request.headers.get("X-Timestamp")
      signature = request.headers.get("X-Signature")

      if not timestamp or not signature:
          return jsonify({"error": "UNAUTHORIZED", "errorMessage": "Missing signature headers"}), 401

      # Validate timestamp window (5 minutes)
      now = int(time.time())
      ts = int(timestamp)
      if abs(now - ts) > 300:
          return jsonify({"error": "UNAUTHORIZED", "errorMessage": "Timestamp outside acceptable window"}), 401

      payload = request.get_data(as_text=True) + timestamp
      expected = hmac.new(
          VTPE_HMAC_SECRET.encode("utf-8"),
          payload.encode("utf-8"),
          hashlib.sha256
      ).hexdigest()

      if not hmac.compare_digest(signature, expected):
          return jsonify({"error": "UNAUTHORIZED", "errorMessage": "Invalid signature"}), 401

  # Product Information API
  @app.route("/payments/<reference>")
  def get_payment(reference):
      order = db.find_order(reference)

      if not order:
          return jsonify({"error": "NOT_FOUND", "errorMessage": "Reference not found"}), 404
      if order.is_paid:
          return jsonify({"error": "ALREADY_PAID", "errorMessage": "Payment already completed"}), 409

      return jsonify({
          "reference": order.reference,
          "total_amount": order.total_amount,
          "currency": order.currency,
          "amount_detail": order.amount_detail or [],
          "details": order.details or []
      })

  # Webhook handler
  @app.route("/webhooks/vtpe", methods=["POST"])
  def handle_webhook():
      sig_error = verify_signature()
      if sig_error:
          return sig_error

      payload = request.get_json()
      event = payload["event"]
      data = payload["data"]

      if event == "payment.initialized":
          db.create_pending_payment(
              payment_id=data["paymentId"],
              reference=data["reference"],
              amount=data["amount"],
              currency=data["currency"],
              status="pending"
          )

      if event == "payment.success":
          if db.get_payment_by_payment_id(data["paymentId"]):
              return jsonify({"success": True}), 200

          db.fulfill_order(
              reference=data["reference"],
              payment_id=data["paymentId"],
              paid_at=data["paidAt"],
              channel=data["channel"]
          )

      if event == "payment.fail":
          db.cancel_order(
              reference=data["reference"],
              payment_id=data["paymentId"]
          )

      return jsonify({"success": True}), 200

  if __name__ == "__main__":
      app.run(port=3000)
  ```
</CodeGroup>

<Note>
  The database calls (`db.findOrder`, `db.createPendingPayment`, etc.) are pseudocode. Replace them with your actual database client logic.
</Note>

For details on securing your webhooks, see the [Webhook Security guide](/concepts/webhook-security). For production best practices, see the [Best Practices guide](/concepts/best-practices).
