McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Verify Paystack Payments in Firebase Cloud Functions
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Verify Paystack Payments in Firebase Cloud Functions

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.

In this article

  1. 1. Verify Cloud Function
  2. 2. Call from Frontend
  3. 3. Learn More

Verify Cloud Function

// 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" });
      }
    });
  }
);

Call from Frontend

// 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.

Learn More

This guide is part of the Paystack integration guides by language and framework series.

Next: Handle Paystack Webhooks in Firebase Cloud Functions

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Verification runs in the Cloud Function — the secret key never leaves the server.
  • ✓Call GET https://api.paystack.co/transaction/verify/:reference with the Bearer token.
  • ✓Validate data.status === "success" AND data.amount matches your expected amount before granting access.
  • ✓Update Firestore after verification using the Firebase Admin SDK.
  • ✓Use idempotency: check if the order is already marked paid before processing again.
  • ✓Return a clean response to the frontend: verified, amount, currency, reference.

Frequently Asked Questions

Do I need Firebase Admin SDK to update Firestore from a Cloud Function?
Yes. Cloud Functions run in a trusted server environment, and you use the Firebase Admin SDK to access Firestore. Initialize with initializeApp() once at the top of your functions file. Then use getFirestore() to get the database. The Admin SDK bypasses Firestore security rules by default.
What is the difference between verifying via webhook and verifying via a Cloud Function?
Webhook verification is event-driven — Paystack calls your backend when a payment succeeds. Cloud Function verification is request-driven — your frontend calls the function after checkout redirect. Both should be implemented: webhooks for reliability, frontend verification for immediate UX feedback.
Can I pass the reference in the request body instead of the query string?
Yes. The example reads reference from both req.query.reference (GET) and req.body.reference (POST). For a GET request, use the query string. For a POST request from a trusted frontend, use the body. Either approach works.
How do I prevent a Paystack transaction from being verified twice?
Check Firestore before updating: if the order document already has status: "paid", return the result without re-processing. This is the idempotency pattern shown in the example. The already_processed flag in the response lets your frontend know the order was already confirmed.

Learn Paystack Integration

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

Not ready? Create Free Account

Keep reading

Paystack Integration Guides by Language and Framework

A complete directory of Paystack integration guides organized by programming language and framework. Each guide covers accepting payments, verifying transactions, handling webhooks, and building subscriptions in your specific stack.

Read more

Accept Payments with Paystack in Firebase Cloud Functions

Firebase Cloud Functions provide a Node.js backend for Paystack without a dedicated server. This guide covers setting up a Cloud Function to initialize Paystack transactions, storing secrets securely, configuring CORS, and calling from a frontend.

Read more

Handle Paystack Webhooks in Firebase Cloud Functions

Handle Paystack webhook events in Firebase Cloud Functions. This guide covers HMAC SHA512 signature verification using Node.js crypto, reading the raw request body, returning 200 before processing, and updating Firestore with payment results.

Read more

Build Paystack Subscriptions in Firebase Cloud Functions

Add recurring billing to a Firebase project using Paystack subscriptions and Cloud Functions. This guide covers subscription checkout with a plan code, webhook handling for subscription.create and invoice.payment_failed, and checking subscription status from Firestore.

Read more

Transaction Not Found on Paystack Verify

Paystack returns "Transaction not found" when the reference you pass to the verify endpoint does not match any transaction in the current environment. The causes range from wrong reference strings to test/live key confusion to URL encoding problems. This guide covers every cause with specific fixes.

Read more

Idempotency in Paystack Payment Flows

Duplicate charges are the nightmare scenario for any payment integration. A customer clicks pay twice, a network timeout triggers a retry, or a webhook fires before the redirect callback. Idempotency is the engineering discipline that makes sure the same payment operation produces the same result no matter how many times it runs.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.