Infinity Africa

Examples

JavaScript Example

A minimal Node.js client — no SDK, just fetch. Works the same from an Express/Next.js backend powering a website, an ecommerce checkout, or a mobile app's API layer.

info

Keep this code on your server

This example assumes it's running in a Node.js backend, not a browser — your API key must never reach client-side JavaScript. See API Key Authentication.

A small client wrapper

javascript — infinity-client.js
const BASE_URL = process.env.INFINITY_BASE_URL ?? "https://api.infinityafrica.net";
const API_KEY = process.env.INFINITY_API_KEY;

async function infinityRequest(method, path, { body, idempotencyKey } = {}) {
  const headers = {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json",
  };
  if (idempotencyKey) headers["Idempotency-Key"] = idempotencyKey;

  const response = await fetch(`${BASE_URL}${path}`, {
    method,
    headers,
    body: body ? JSON.stringify(body) : undefined,
  });

  const envelope = await response.json();
  if (!envelope.success) {
    const error = new Error(envelope.error.message);
    error.code = envelope.error.code;
    error.status = response.status;
    throw error;
  }
  return envelope.data;
}

module.exports = { infinityRequest };

Create a payment link at checkout

javascript
const { randomUUID } = require("crypto");
const { infinityRequest } = require("./infinity-client");

async function createCheckoutLink(order) {
  const link = await infinityRequest("POST", "/v1/payment-links", {
    idempotencyKey: randomUUID(),
    body: {
      merchant_id: process.env.INFINITY_MERCHANT_ID,
      amount: order.total.toFixed(2),
      currency: "TZS",
      customer_name: order.customerName,
      customer_phone: order.customerPhone,
      description: `Order #${order.id}`,
      expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
    },
  });

  return link.public_url; // redirect your customer here
}

Handling errors

javascript
try {
  await createCheckoutLink(order);
} catch (err) {
  if (err.code === "insufficient_balance") {
    // won't happen for payment links, but this is the pattern for disbursements
  } else if (err.code === "validation_error") {
    console.error("Bad request:", err.message);
  } else {
    throw err; // let it bubble, retry, or alert — depending on your app
  }
}

Receiving webhooks (Express)

javascript
const express = require("express");
const crypto = require("crypto");

const app = express();

// Capture the raw body — signature verification needs the exact bytes,
// not JSON re-serialized after parsing.
app.post(
  "/webhooks/infinity",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.header("X-Infinity-Signature");
    const expected = crypto
      .createHmac("sha256", process.env.INFINITY_WEBHOOK_SECRET)
      .update(req.body)
      .digest("hex");

    if (signature !== expected) {
      return res.sendStatus(401);
    }

    const event = JSON.parse(req.body);
    switch (event.event_name) {
      case "collection.success":
        // mark the order as paid using event.payload.collection_id
        break;
      case "disbursement.failed":
        // alert someone — a payout didn't go through
        break;
    }

    res.sendStatus(200); // acknowledge fast; do heavy work asynchronously
  },
);