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 Vue
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Verify Paystack Payments in Vue

Vue cannot verify Paystack payments directly because verification requires your secret key. Build a backend endpoint that calls Paystack's Verify Transaction API, then call that endpoint from Vue after the popup callback or redirect. Check that data.data.status equals "success" and that the amount matches what you expected.

In this article

  1. 1. Why Verification Happens on the Backend
  2. 2. Build the Backend Verification Endpoint
  3. 3. Verify After the Inline Popup
  4. 4. Verify After Redirect Checkout
  5. 5. Reusable Verification Composable
  6. 6. Amount Validation Is Not Optional
  7. 7. Webhooks as Your Safety Net
  8. 8. Ship Payments Faster

Why Verification Happens on the Backend

When a customer finishes paying through the Paystack popup, your Vue component receives a callback with the transaction reference. That callback runs in the browser. It tells you the customer completed the payment form, but it does not prove the money actually landed. A user could fake the callback, or the network could drop before Paystack finished processing.

The Paystack Verify Transaction API (GET /transaction/verify/:reference) is the only reliable way to confirm a payment. It requires your secret key in the Authorization header. That key must never appear in client-side code. So your Vue app calls your own backend, and your backend calls Paystack.

This guide uses Express for the backend examples, but the pattern is the same for Django, Laravel, FastAPI, or any server framework. The Vue side stays identical regardless of backend choice.

Build the Backend Verification Endpoint

Create a simple endpoint that takes a reference, calls Paystack, and returns the result:

// server.js (Express)
const express = require('express');
const app = express();

const PAYSTACK_SECRET = process.env.PAYSTACK_SECRET_KEY;

app.get('/api/verify', async (req, res) => {
  var reference = req.query.reference;

  if (!reference) {
    return res.status(400).json({ error: 'Reference is required' });
  }

  var response = await fetch(
    'https://api.paystack.co/transaction/verify/' + encodeURIComponent(reference),
    {
      headers: { Authorization: 'Bearer ' + PAYSTACK_SECRET },
    }
  );

  var data = await response.json();

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

  var txn = data.data;

  // Look up the expected amount from your database
  // var order = await db.orders.findOne({ reference: reference });
  // if (txn.amount !== order.expectedAmountInKobo) { return res.status(400).json({ verified: false }); }

  if (txn.status === 'success') {
    return res.json({
      verified: true,
      amount: txn.amount / 100,
      currency: txn.currency,
      reference: txn.reference,
      channel: txn.channel,
      paidAt: txn.paid_at,
    });
  }

  res.status(400).json({ verified: false, status: txn.status });
});

app.listen(4000);

The endpoint does three things: calls Paystack, checks the status, and validates the amount. The amount check (commented out above) is critical in production. Without it, a customer could initialize a transaction for 100 NGN and you would verify a 100 NGN payment when they should have paid 10,000 NGN.

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Verify After the Inline Popup

In your Vue component, call your backend verification endpoint inside the popup's onSuccess callback:

<script setup>
import { ref } from 'vue';

var API_URL = import.meta.env.VITE_API_URL || 'http://localhost:4000';
var verifyState = ref('idle'); // idle | verifying | success | failed
var verifyResult = ref(null);

async function onPaystackSuccess(transaction) {
  verifyState.value = 'verifying';

  try {
    var res = await fetch(
      API_URL + '/api/verify?reference=' + transaction.reference
    );
    var data = await res.json();

    if (data.verified) {
      verifyState.value = 'success';
      verifyResult.value = data;
    } else {
      verifyState.value = 'failed';
    }
  } catch (err) {
    verifyState.value = 'failed';
  }
}
</script>

<template>
  <div v-if="verifyState === 'verifying'">
    <p>Verifying your payment...</p>
  </div>
  <div v-else-if="verifyState === 'success'">
    <h2>Payment Confirmed</h2>
    <p>{{ verifyResult.currency }} {{ verifyResult.amount }} received.</p>
    <p>Reference: {{ verifyResult.reference }}</p>
  </div>
  <div v-else-if="verifyState === 'failed'">
    <p>Verification failed. Contact support with your reference number.</p>
  </div>
</template>

The verifyState ref drives the UI. The user sees a loading indicator during verification, a confirmation on success, and a clear error message on failure. Keep the reference visible so the customer can share it with support if needed.

Verify After Redirect Checkout

When you use redirect checkout instead of the inline popup, Paystack sends the customer back to your callback URL with ?trxref=xxx&reference=xxx in the query string. Create a callback page in Vue Router that verifies on mount:

<!-- src/views/PaymentCallback.vue -->
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';

var route = useRoute();
var router = useRouter();
var API_URL = import.meta.env.VITE_API_URL || 'http://localhost:4000';

var status = ref('verifying');
var result = ref(null);

onMounted(async function() {
  var reference = route.query.reference || route.query.trxref;

  if (!reference) {
    status.value = 'error';
    return;
  }

  try {
    var res = await fetch(
      API_URL + '/api/verify?reference=' + reference
    );
    var data = await res.json();

    if (data.verified) {
      status.value = 'success';
      result.value = data;
    } else {
      status.value = 'failed';
    }
  } catch (err) {
    status.value = 'error';
  }
});
</script>

<template>
  <div v-if="status === 'verifying'">Verifying payment...</div>
  <div v-else-if="status === 'success'">
    <h1>Payment Successful</h1>
    <p>{{ result.currency }} {{ result.amount }}</p>
  </div>
  <div v-else>
    <p>Something went wrong. Please contact support.</p>
  </div>
</template>

Register this route in your Vue Router config:

// router/index.js
{ path: '/payment/callback', component: () => import('../views/PaymentCallback.vue') }

Set this URL as the callback_url when initializing the transaction on your backend.

Reusable Verification Composable

If you verify payments in multiple places, extract the logic:

// src/composables/usePaystackVerify.js
import { ref } from 'vue';

var API_URL = import.meta.env.VITE_API_URL || 'http://localhost:4000';

export function usePaystackVerify() {
  var loading = ref(false);
  var verified = ref(false);
  var error = ref(null);
  var transaction = ref(null);

  async function verify(reference) {
    loading.value = true;
    error.value = null;

    try {
      var res = await fetch(
        API_URL + '/api/verify?reference=' + reference
      );
      var data = await res.json();

      if (data.verified) {
        verified.value = true;
        transaction.value = data;
      } else {
        error.value = 'Payment not confirmed';
      }
    } catch (err) {
      error.value = 'Network error during verification';
    } finally {
      loading.value = false;
    }
  }

  return { verify, loading, verified, error, transaction };
}

Use it in any component with a single line: var { verify, loading, verified, transaction } = usePaystackVerify();

Amount Validation Is Not Optional

Verifying that status === "success" is necessary but not sufficient. You must also check that the amount matches what you charged. Here is why:

  1. Your Vue app sends amount: 10000 (100 NGN in kobo) to your backend.
  2. An attacker intercepts the request and changes it to amount: 100 (1 NGN).
  3. Paystack initializes a 1 NGN transaction. The attacker pays 1 NGN.
  4. Your backend verifies the reference. Paystack says "success." You deliver the product.

The fix: your backend should look up the correct price from your database, not from the frontend request. When verifying, compare data.data.amount against the expected amount in your database.

// In your verify endpoint
var txn = data.data;
var order = await db.orders.findOne({ reference: reference });

if (txn.status === 'success' && txn.amount === order.amountInKobo) {
  order.status = 'paid';
  await order.save();
  return res.json({ verified: true, amount: txn.amount / 100 });
}

return res.status(400).json({ verified: false, reason: 'Amount mismatch or payment not successful' });

Webhooks as Your Safety Net

Frontend verification can fail. The customer might close their browser after paying, or your backend could be temporarily down when the popup callback fires. Webhooks solve this.

Paystack sends a charge.success webhook to your backend independently of the frontend flow. Your backend should be able to fulfill orders from either the verification call or the webhook, whichever arrives first. Use a database flag to prevent double fulfillment.

Vue cannot receive webhooks because it runs in the browser. See Handle Paystack Webhooks in Vue for the architectural explanation and links to backend webhook guides.

Ship Payments Faster

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

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Vue runs in the browser. Verification must happen on your backend because it requires the Paystack secret key.
  • ✓After the Paystack popup onSuccess callback fires, call your backend to verify. Do not trust the popup callback alone.
  • ✓Your backend calls GET https://api.paystack.co/transaction/verify/:reference and checks that data.data.status is "success".
  • ✓Always compare the verified amount and currency against your database records. A customer could tamper with frontend values.
  • ✓For redirect checkout, read the reference from the URL query string on your callback page and verify immediately on mount.
  • ✓Use reactive refs for verification state so your UI shows loading, success, or error states clearly.
  • ✓Webhooks are the ultimate safety net. Even if verification from the frontend fails, the webhook will confirm the payment.

Frequently Asked Questions

Can I verify a Paystack payment directly from Vue without a backend?
No. The Verify Transaction API requires your Paystack secret key in the Authorization header. If you call it from Vue, you expose the key to anyone who opens browser DevTools. You must call it from your backend.
What does the Paystack Verify API return?
It returns a JSON object with data.status, data.amount (in kobo/pesewas/cents), data.currency, data.reference, data.channel, data.paid_at, and more. Check data.data.status for "success" to confirm the payment went through.
Should I verify on the frontend or rely on webhooks only?
Both. Verify from the frontend for immediate user feedback. Use webhooks as the reliable backend confirmation. Either path should be able to fulfill the order independently, with a database flag to prevent double fulfillment.
What if the verification call times out?
Show the customer a message like "We are confirming your payment. You will receive a confirmation email shortly." Your webhook handler will pick up the payment and fulfill the order. Do not charge the customer again.
How do I handle the case where the customer paid but the amount does not match?
This usually means someone tampered with the amount on the frontend. Log the mismatch, do not fulfill the order, and flag it for manual review. The customer can contact support with their reference number.

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

Verify Paystack Payments in Svelte and SvelteKit

Verify Paystack transactions in SvelteKit using server endpoints for popup verification and load functions for redirect callback verification. Covers amount validation, idempotent fulfillment, and SSR verification for instant results.

Read more

Handle Paystack Webhooks in Flask

Receive and verify Paystack webhooks in your Flask application. This guide covers accessing request.data for raw bytes, HMAC SHA-512 signature verification, processing charge.success events, and making handlers idempotent.

Read more

Build Paystack Subscriptions in ASP.NET Core

Implement Paystack recurring billing in ASP.NET Core. This guide covers creating plans, subscribing customers through transaction initialization, handling renewal webhooks with C#, and cancelling subscriptions.

Read more

Paystack Transaction Timeline API for Debugging Customer Complaints

A customer says they paid but did not get their product. Another says they were charged twice. Your support team needs answers. The Paystack Transaction Timeline API shows you exactly what happened at each step of a transaction, from initialization to settlement. This guide shows you how to use it for debugging and customer support.

Read more

Debugging Paystack with the Paystack CLI

The Paystack CLI gives you terminal access to your Paystack account. Instead of clicking through the Dashboard to check a transaction or waiting for a webhook to arrive, you run a command and get the answer in seconds. For developers who live in the terminal, it is the fastest way to debug payment issues.

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.