Bonaventure OgetoBy Bonaventure Ogeto|

Accept Payments with Paystack in Firebase Cloud Functions

Create a Firebase Cloud Function using onRequest (v2). Store your Paystack secret key with firebase functions:secrets:set PAYSTACK_SECRET_KEY. In the function, access it with process.env.PAYSTACK_SECRET_KEY, call the Paystack initialize endpoint using native fetch (Node 18+), and return the authorization URL to your frontend.

Setup: Secret and Project

# Initialize Cloud Functions if not already done
firebase init functions
# Choose TypeScript, Node 18

# Store Paystack secret key as a Firebase secret
firebase functions:secrets:set PAYSTACK_SECRET_KEY

# Install dependencies
cd functions
npm install cors
npm install --save-dev @types/cors

Initialize Transaction Function

// functions/src/index.ts
import { onRequest } from "firebase-functions/v2/https";
import { defineSecret } from "firebase-functions/params";
import * as cors from "cors";

const paystackSecretKey = defineSecret("PAYSTACK_SECRET_KEY");
const corsHandler = cors({ origin: true });

export const initializePayment = onRequest(
  { secrets: [paystackSecretKey] },
  async (req, res) => {
    corsHandler(req, res, async () => {
      if (req.method !== "POST") {
        res.status(405).send("Method not allowed");
        return;
      }

      const secretKey = paystackSecretKey.value();
      const { email, amount } = req.body;

      if (!email || !amount) {
        res.status(400).json({ error: "email and amount are required" });
        return;
      }

      const reference = "fcn_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);

      try {
        const paystackRes = await fetch(
          "https://api.paystack.co/transaction/initialize",
          {
            method: "POST",
            headers: {
              Authorization: "Bearer " + secretKey,
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              email,
              amount: Math.round(Number(amount) * 100),
              reference,
            }),
          }
        );

        const data = await paystackRes.json();

        if (!data.status) {
          res.status(400).json({ error: data.message });
          return;
        }

        res.json({
          authorization_url: data.data.authorization_url,
          access_code: data.data.access_code,
          reference: data.data.reference,
        });
      } catch (error) {
        res.status(500).json({ error: "Failed to initialize payment" });
      }
    });
  }
);

Deploy and Call from Frontend

# Deploy the function
firebase deploy --only functions:initializePayment

# Get the function URL from the console or CLI output:
# https://<region>-<project-id>.cloudfunctions.net/initializePayment
// Frontend call
async function initializePayment(email: string, amount: number) {
  var response = await fetch(
    "https://<region>-<project-id>.cloudfunctions.net/initializePayment",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, amount }),
    }
  )

  var data = await response.json()
  // data.authorization_url — redirect or open in WebView
  window.location.href = data.authorization_url
}

Learn More

Key Takeaways

  • Use Firebase Cloud Functions v2 (onRequest) for Paystack backend. Node 18+ includes native fetch.
  • Store the secret key with: firebase functions:secrets:set PAYSTACK_SECRET_KEY
  • Declare the secret in your function: { secrets: ["PAYSTACK_SECRET_KEY"] }. Access with process.env.PAYSTACK_SECRET_KEY.
  • Handle CORS in the function using the cors npm package or manual header setting.
  • Deploy with: firebase deploy --only functions:initializePayment
  • Call the function URL from your frontend. Get it from the Firebase console or CLI output.

Frequently Asked Questions

Should I use Firebase Cloud Functions v1 or v2 for Paystack?
Use v2 (firebase-functions/v2/https). It has native secret management with defineSecret, runs on Node 18 with native fetch, and has better cold start performance. V1 is legacy. The code pattern is slightly different — v2 uses onRequest() as a named export, not functions.https.onRequest().
How do I store the Paystack secret key in Firebase?
Use Firebase Secrets: run firebase functions:secrets:set PAYSTACK_SECRET_KEY in your terminal and paste the key when prompted. In your function, declare it with defineSecret("PAYSTACK_SECRET_KEY") and pass it to the secrets array in onRequest options. Access the value with paystackSecretKey.value() inside the function.
Do I need to install node-fetch in Firebase Cloud Functions?
Not if you use Node 18 or above (the default for v2 functions). Node 18 includes native fetch. If your functions are still on Node 16, install node-fetch@2 (CommonJS compatible) with npm install node-fetch@2.
How do I handle CORS for Paystack Cloud Functions called from a browser?
Install the cors npm package and wrap your handler with corsHandler(req, res, async () => { ... }). For simple cases, you can also set headers manually: res.set("Access-Control-Allow-Origin", "*") before returning the response. Handle OPTIONS preflight by returning 204 with the CORS headers.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon