> ## Documentation Index
> Fetch the complete documentation index at: https://docs.billstack.co/llms.txt
> Use this file to discover all available pages before exploring further.

# 💡 Introduction

BillStack is a developer-first payment gateway that enables businesses to collect payments through dedicated virtual bank accounts. Whether you're building a fintech product, an e-commerce platform, or an internal billing system, BillStack gives you the tools to issue virtual accounts, track incoming payments, and react to transactions in real time through webhooks - all through a clean, predictable REST API.

<CardGroup cols={2}>
  <Card title="Quick Start" icon="rocket" href="/quickstart">
    Go from zero to your first successful payment in under 10 minutes.
  </Card>

  <Card title="Authentication" icon="key" href="/authentication">
    Secure your API calls with your BillStack public and secret keys.
  </Card>

  <Card title="Virtual Accounts" icon="building-columns" href="/">
    Understand how virtual accounts work and when to use them.
  </Card>

  <Card title="API Reference" icon="code" href="/overview">
    Explore the full BillStack REST API - endpoints, parameters, and responses.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/webhooks">
    Receive real-time payment notifications delivered straight to your server.
  </Card>

  <Card title="Create a Virtual Account" icon="book" href="/create-account">
    Follow a step-by-step guide to issuing your first virtual account.
  </Card>
</CardGroup>

## How BillStack works

Getting up and running with BillStack takes four steps. Once you complete them, your integration will be able to issue virtual accounts to customers and automatically respond to incoming payments.

<Steps>
  <Step title="Get your API keys">
    Sign in at [dashboard.billstack.co](https://api.billstack.co) and retrieve your API keys from **Settings → API Keys**. Each business has a **public key** (used for client-side and checkout flows) and a **secret key** (used to authenticate server-side API calls and to verify webhook signatures). Keep your secret key on your server - never expose it in client code.
  </Step>

  <Step title="Create a virtual account">
    Call the virtual accounts endpoint to generate a unique bank account number for a customer or order. BillStack returns a NUBAN account number, the bank it belongs to, and the reference you supplied, so your customer can pay into it immediately.
  </Step>

  <Step title="Receive a payment">
    When your customer makes a bank transfer to their virtual account, BillStack detects the credit and records the transaction against the account reference you provided.
  </Step>

  <Step title="Handle the webhook">
    BillStack sends a payment event to your configured webhook URL the moment funds arrive. Verify the signature, parse the payload, and fulfil the order - all in real time.
  </Step>
</Steps>

<Note>
  New to BillStack? The [Quick Start guide](/quickstart) walks through all four steps above with live code examples so you can test the full flow before going live.
</Note>

## Verifying webhooks

Every webhook BillStack sends is signed so you can confirm it genuinely came from us and was not tampered with in transit. Each request includes these headers:

| Header                  | Description                                                                                                                            |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `x-wiaxy-signature-256` | The signature to verify. An HMAC-SHA256 (hex) of the string `"{timestamp}.{rawBody}"`, keyed with your **secret key**.                 |
| `x-wiaxy-timestamp`     | The Unix timestamp (seconds) used in the signed string. Reject requests whose timestamp is older than a few minutes to prevent replay. |
| `x-wiaxy-signature`     | A legacy static signature, `md5(secret_key)`, retained for backward compatibility. Prefer `x-wiaxy-signature-256`.                     |

To verify, recompute the HMAC over the **raw request body** (exactly as received, before any JSON parsing) prefixed with the timestamp, then compare it to `x-wiaxy-signature-256` using a constant-time comparison.

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

  function verifyBillStackWebhook(rawBody, headers, secretKey) {
    const timestamp = headers["x-wiaxy-timestamp"];
    const signature = headers["x-wiaxy-signature-256"];

    const signedString = `${timestamp}.${rawBody}`;
    const expected = crypto
      .createHmac("sha256", secretKey)
      .update(signedString)
      .digest("hex");

    // Constant-time compare to avoid timing attacks.
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expected)
    );
  }
  ```

  ```php PHP theme={null}
  function verify_billstack_webhook($rawBody, $headers, $secretKey) {
      $timestamp = $headers['x-wiaxy-timestamp'] ?? '';
      $signature = $headers['x-wiaxy-signature-256'] ?? '';

      $signedString = $timestamp . '.' . $rawBody;
      $expected = hash_hmac('sha256', $signedString, $secretKey);

      return hash_equals($expected, $signature);
  }
  ```
</CodeGroup>

<Warning>
  Always sign and verify against the **raw, unparsed request body**. Re-serialising the JSON (for example, `JSON.stringify(req.body)`) can reorder keys or change whitespace, which produces a different signature and a failed check.
</Warning>
