Create a Firebase Cloud Function using onRequest (v2). Read the reference from the query string or request body, call GET https://api.paystack.co/transaction/verify/:reference with your PAYSTACK_SECRET_KEY, check data.status === "success", update Firestore with the payment result, and return verified status to your frontend.
// functions/src/index.ts
import { onRequest } from "firebase-functions/v2/https";
import { defineSecret } from "firebase-functions/params";
import { getFirestore } from "firebase-admin/firestore";
import { initializeApp } from "firebase-admin/app";
import * as cors from "cors";
initializeApp();
const paystackSecretKey = defineSecret("PAYSTACK_SECRET_KEY");
const corsHandler = cors({ origin: true });
export const verifyPayment = onRequest(
{ secrets: [paystackSecretKey] },
async (req, res) => {
corsHandler(req, res, async () => {
const reference = req.query.reference as string || req.body?.reference;
if (!reference) {
res.status(400).json({ error: "Reference is required" });
return;
}
const secretKey = paystackSecretKey.value();
try {
const paystackRes = await fetch(
"https://api.paystack.co/transaction/verify/" + encodeURIComponent(reference),
{
headers: { Authorization: "Bearer " + secretKey },
}
);
const paystackData = await paystackRes.json();
if (!paystackData.status || paystackData.data.status !== "success") {
res.status(400).json({
verified: false,
status: paystackData.data?.status,
message: paystackData.data?.gateway_response || "Payment not verified",
});
return;
}
const txData = paystackData.data;
const db = getFirestore();
// Check idempotency
const orderRef = db.collection("orders").doc(reference);
const order = await orderRef.get();
if (order.exists && order.data()?.status === "paid") {
res.json({
verified: true,
amount: txData.amount / 100,
currency: txData.currency,
reference: txData.reference,
already_processed: true,
});
return;
}
// Update Firestore
await orderRef.set({
status: "paid",
paid_at: new Date().toISOString(),
amount_paid: txData.amount / 100,
currency: txData.currency,
customer_email: txData.customer.email,
}, { merge: true });
res.json({
verified: true,
amount: txData.amount / 100,
currency: txData.currency,
reference: txData.reference,
customer_email: txData.customer.email,
});
} catch (error) {
res.status(500).json({ error: "Verification failed" });
}
});
}
);
// After Paystack checkout redirect
async function verifyPayment(reference: string) {
var response = await fetch(
"https://<region>-<project-id>.cloudfunctions.net/verifyPayment?reference=" + reference
)
var result = await response.json()
if (result.verified) {
// Grant access, show success screen
console.log("Payment confirmed:", result.amount, result.currency)
} else {
// Show failure or retry
console.error("Verification failed:", result.message)
}
}
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
This guide is part of the Paystack integration guides by language and framework series.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course