> ## 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.

# Implement Your First VTPE Integration in Minutes

> Step-by-step guide to implement the Product Information API and webhook handler for VTPE, from creating a partner account to going live.

This quickstart walks you through building a complete VTPE integration. You will implement a Product Information API endpoint that VTPE calls to retrieve payment details, and a webhook handler that receives payment state change events.

## Prerequisites

Before you begin, make sure you have:

* A partner account at [partner.mizaniyapay.dz](https://partner.mizaniyapay.dz/)
* Your **API Secret** for Bearer token authentication
* Your **HMAC Secret** for verifying webhook signatures
* An HTTPS-accessible server to host your endpoints

<Steps>
  <Step title="Create a Partner Account">
    Register at the [Partner Portal](https://partner.mizaniyapay.dz/). Once your account is approved by Mizaniya Pay, you will receive your **API Secret** and **HMAC Secret**. Store both values securely as environment variables before proceeding.

    ```bash theme={null}
    export VTPE_API_SECRET="your_api_secret"
    export VTPE_HMAC_SECRET="your_hmac_secret"
    ```
  </Step>

  <Step title="Configure Your Integration">
    Provide the following two URLs to Mizaniya Pay so VTPE knows where to route requests:

    | Field       | Example                                 | Description                                     |
    | ----------- | --------------------------------------- | ----------------------------------------------- |
    | API URL     | `https://api.example.com/payments`      | Endpoint VTPE calls to retrieve payment info    |
    | Webhook URL | `https://api.example.com/webhooks/vtpe` | Endpoint that receives payment lifecycle events |
  </Step>

  <Step title="Implement the Product Information API">
    Expose a `GET` endpoint at `{API_URL}/{reference}`. VTPE calls this before initiating any payment. Return the payment details or the appropriate error response.

    <CodeGroup>
      ```javascript Node.js theme={null}
      import express from "express";
      const app = express();
      app.use(express.json());

      function authenticate(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();
      }

      app.get("/payments/:reference", authenticate, (req, res) => {
        const { reference } = req.params;

        // Replace with your real database lookup
        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.status(200).json({
          reference: order.reference,
          total_amount: order.totalAmount,
          currency: order.currency,
          amount_detail: order.amountDetail ?? [],
          details: order.details ?? []
        });
      });

      app.listen(3000);
      ```

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

      app = Flask(__name__)

      def authenticate():
          auth = request.headers.get("Authorization", "")
          if auth != f"Bearer {os.environ['VTPE_API_SECRET']}":
              return jsonify({"error": "UNAUTHORIZED", "errorMessage": "Invalid or missing API Secret"}), 401

      @app.route("/payments/<reference>")
      def get_payment(reference):
          error = authenticate()
          if error:
              return error

          # Replace with your real database lookup
          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 []
          })
      ```
    </CodeGroup>

    <Note>
      See [Errors](/concepts/errors) for the full list of error codes and when to return each one.
    </Note>
  </Step>

  <Step title="Implement the Webhook Handler">
    VTPE POSTs signed webhook events to your Webhook URL. Verify the signature, parse the `event` and `data` fields, handle each event type, and return HTTP 200 with `{ "success": true }`.

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

      // Use raw body middleware BEFORE JSON parsing
      app.use("/webhooks/vtpe", express.raw({ type: "application/json" }));

      app.post("/webhooks/vtpe", (req, res) => {
        const rawBody = req.body.toString("utf8");
        const timestamp = req.headers["x-timestamp"] ?? "";
        const receivedSig = req.headers["x-signature"] ?? "";

        const expectedSig = crypto
          .createHmac("sha256", process.env.VTPE_HMAC_SECRET)
          .update(rawBody + timestamp)
          .digest("hex");

        if (!crypto.timingSafeEqual(Buffer.from(expectedSig), Buffer.from(receivedSig))) {
          return res.status(400).json({ error: "Invalid signature" });
        }

        const { event, data } = JSON.parse(rawBody);

        switch (event) {
          case "payment.initialized":
            // Create a pending payment record
            console.log("Payment initialized:", data.paymentId);
            break;
          case "payment.success":
            // Fulfill the order
            console.log("Payment success:", data.reference, "via", data.channel);
            break;
          case "payment.fail":
            // Release reservations
            console.log("Payment failed:", data.paymentId);
            break;
          default:
            console.log("Unknown event:", event);
        }

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

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

      app = Flask(__name__)

      @app.route("/webhooks/vtpe", methods=["POST"])
      def handle_webhook():
          raw_body = request.get_data(as_text=True)
          timestamp = request.headers.get("X-Timestamp", "")
          received_sig = request.headers.get("X-Signature", "")

          expected_sig = hmac.new(
              os.environ["VTPE_HMAC_SECRET"].encode(),
              (raw_body + timestamp).encode(),
              hashlib.sha256
          ).hexdigest()

          if not hmac.compare_digest(expected_sig, received_sig):
              return jsonify({"error": "Invalid signature"}), 400

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

          if event == "payment.initialized":
              print(f"Payment initialized: {data['paymentId']}")
          elif event == "payment.success":
              print(f"Payment success: {data['reference']} via {data['channel']}")
          elif event == "payment.fail":
              print(f"Payment failed: {data['paymentId']}")
          else:
              print(f"Unknown event: {event}")

          return jsonify({"success": True}), 200
      ```
    </CodeGroup>
  </Step>

  <Step title="Verify Signatures">
    VTPE signs every webhook using `HMAC-SHA256(rawBody + timestamp, HMAC_SECRET)`. The result is a plain hex string in the `X-Signature` header. Always verify this before processing any event.

    For the full verification guide with step-by-step instructions, see [Webhook Security](/guides/webhook-security).
  </Step>

  <Step title="Go Live">
    Before switching to production, complete this checklist:

    * Switch to your production API Secret and HMAC Secret
    * Confirm both your API URL and Webhook URL are HTTPS
    * Test the full payment flow with Mizaniya Pay
    * Verify signature verification is active (never skip in production)
  </Step>
</Steps>

## Next Steps

* Review the [API Reference](/api-reference/payments/get-payment) for complete request and response fields.
* Read the [Webhook Security guide](/guides/webhook-security) for full HMAC signature verification details.
* Follow [Best Practices](/guides/best-practices) to make your integration robust and production-ready.
