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
| Parameter | Description |
|---|---|
ref required | The transaction reference from onSuccess callback or webhook payload. |
Response Fields
| Field | Type | Description |
|---|---|---|
status | string | successful, pending, or failed |
transaction_ref | string | The unique reference for this transaction |
amount | number | Amount in the lowest denomination (kobo for NGN) |
currency | string | ISO 4217 currency code (e.g. NGN) |
customer | string | Customer email address |
created_at | string | ISO 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);