Flowwithlit Docs
ACCEPT PAYMENTS

Verify Transactions

After a customer pays, verify the transaction on your server before delivering anything. The browser callback alone is not enough — it can be faked.

🚨
Never skip server-side verification. A technically savvy customer can fire the onSuccess callback manually in the browser console without actually paying. Always call the verify endpoint from your server before fulfilling orders.

Endpoint

GET /v1/transaction/verify/{ref} 🔑 Secret Key

Looks up a transaction by its reference and returns the current status. Authenticates using your secret key as a Bearer token — call this from your server only.

Path Parameters

ParameterDescription
ref requiredThe transaction reference from onSuccess callback or webhook payload.

Response Fields

FieldTypeDescription
statusstringsuccessful, pending, or failed
transaction_refstringThe unique reference for this transaction
amountnumberAmount in the lowest denomination (kobo for NGN)
currencystringISO 4217 currency code (e.g. NGN)
customerstringCustomer email address
created_atstringISO 8601 timestamp of the transaction

Code Examples

<?php
function verifyTransaction(string $ref): array {
    $ch = curl_init(
        'https://api.flowwithlit.com/v1/transaction/verify/' . urlencode($ref)
    );
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . getenv('FLW_SECRET_KEY'),
        ],
        CURLOPT_TIMEOUT        => 10,
    ]);

    $body = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $res = json_decode($body, true);

    if ($code === 200 && ($res['data']['status'] ?? '') === 'successful') {
        return ['verified' => true, 'data' => $res['data']];
    }

    return ['verified' => false, 'message' => $res['message'] ?? 'Verification failed'];
}

// Usage
$result = verifyTransaction('FLW_TXN_ABCD1234');
if ($result['verified']) {
    $amountNaira = $result['data']['amount'] / 100;
    // fulfil order, e.g. mark as paid in DB
    echo "Payment of ₦$amountNaira confirmed!";
}
const fetch = require('node-fetch'); // npm i node-fetch@2

async function verifyTransaction(ref) {
  const res  = await fetch(
    `https://api.flowwithlit.com/v1/transaction/verify/${encodeURIComponent(ref)}`,
    { headers: { Authorization: `Bearer ${process.env.FLW_SECRET_KEY}` } }
  );
  const data = await res.json();

  if (res.ok && data.data?.status === 'successful') {
    return { verified: true, data: data.data };
  }
  return { verified: false, message: data.message };
}

// Usage (async context)
const result = await verifyTransaction('FLW_TXN_ABCD1234');
if (result.verified) {
  const amountNaira = result.data.amount / 100;
  console.log(`Payment of ₦${amountNaira} confirmed`);
  // update your database, send receipt email, etc.
}
import os, requests

def verify_transaction(ref: str) -> dict:
    resp = requests.get(
        f"https://api.flowwithlit.com/v1/transaction/verify/{ref}",
        headers={"Authorization": f"Bearer {os.getenv('FLW_SECRET_KEY')}"},
        timeout=10
    )

    if resp.ok and resp.json().get("data", {}).get("status") == "successful":
        return {"verified": True, "data": resp.json()["data"]}

    return {"verified": False, "message": resp.json().get("message", "failed")}

# Usage
result = verify_transaction("FLW_TXN_ABCD1234")
if result["verified"]:
    amount_naira = result["data"]["amount"] / 100
    print(f"Payment of ₦{amount_naira} confirmed!")
    # fulfil order
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

func verifyTransaction(ref string) (bool, error) {
    req, _ := http.NewRequest("GET",
        "https://api.flowwithlit.com/v1/transaction/verify/"+ref, nil)
    req.Header.Set("Authorization", "Bearer "+os.Getenv("FLW_SECRET_KEY"))

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return false, err
    }
    defer resp.Body.Close()

    var result struct {
        Data struct {
            Status string `json:"status"`
            Amount float64 `json:"amount"`
        } `json:"data"`
    }
    json.NewDecoder(resp.Body).Decode(&result)

    return resp.StatusCode == 200 && result.Data.Status == "successful", nil
}

func main() {
    ok, _ := verifyTransaction("FLW_TXN_ABCD1234")
    if ok {
        fmt.Println("Payment confirmed!")
    }
}
curl -X GET \
  "https://api.flowwithlit.com/v1/transaction/verify/FLW_TXN_ABCD1234" \
  -H "Authorization: Bearer flw_sec_test_YOUR_SECRET_KEY"

Sample Response

JSON — 200 OK
{
  "success": true,
  "message": "Transaction found",
  "data": {
    "status":          "successful",
    "transaction_ref": "FLW_TXN_ABCD1234",
    "amount":          500000,
    "currency":        "NGN",
    "customer":        "john@example.com",
    "created_at":      "2026-06-15T10:32:44Z"
  }
}

Amount Reconciliation

⚠️
Always compare data.amount against the expected amount from your own records. Do not fulfil an order for ₦50 when you expected ₦5,000 — a malicious customer might modify the request. If the amounts don't match, reject the fulfilment and flag the transaction.
PHP — Amount check
$expectedKobo = 500000; // ₦5,000

$result = verifyTransaction($ref);

if (!$result['verified']) {
    die('Payment not confirmed');
}

if ((int)$result['data']['amount'] !== $expectedKobo) {
    // Log this — potential tampering
    die('Amount mismatch — order cannot be fulfilled');
}

// All good — deliver the product
fulfillOrder($orderId);