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

Verify Paystack Payments in Angular

Angular 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 Angular using HttpClient after the popup callback or on the redirect callback page. Always check both the status and amount.

In this article

  1. 1. Why Verification Must Happen on the Backend
  2. 2. Verification Service
  3. 3. Verify After the Popup
  4. 4. Verify on the Redirect Callback Page
  5. 5. Backend Verification Endpoint
  6. 6. Error Handling with HttpInterceptor
  7. 7. Ship Payments Faster

Why Verification Must Happen on the Backend

When a customer finishes paying through the Paystack popup, your Angular component receives a callback with the transaction reference. This callback runs in the browser. It tells you the customer completed the payment form, but it does not prove the money actually landed.

The Paystack Verify Transaction API (GET /transaction/verify/:reference) requires your secret key in the Authorization header. That key must never appear in Angular code. Angular is a client-side framework. Everything in your Angular bundle is visible in the browser. So your Angular app calls your backend, and your backend calls Paystack.

Verification Service

Create a dedicated verification method in your PaystackService:

// src/app/services/paystack.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
import { environment } from '../../environments/environment';

export interface VerifyResult {
  verified: boolean;
  amount: number;
  currency: string;
  reference: string;
  channel: string;
  paidAt: string;
}

@Injectable({ providedIn: 'root' })
export class PaystackService {
  private http = inject(HttpClient);
  private apiUrl = environment.apiUrl;

  async verify(reference: string): Promise<VerifyResult> {
    return firstValueFrom(
      this.http.get<VerifyResult>(this.apiUrl + '/api/verify', {
        params: { reference: reference },
      })
    );
  }
}

The service sends the reference to your backend. Your backend calls Paystack, checks the result, and returns a clean response. The Angular service never touches the Paystack API directly.

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 Popup

// In your checkout component
import { Component, inject, signal } from '@angular/core';
import { PaystackService, VerifyResult } from '../services/paystack.service';

@Component({
  selector: 'app-checkout',
  standalone: true,
  template: '
    <button (click)="handlePay()" [disabled]="verifying()">
      {{ verifying() ? "Verifying..." : "Pay NGN 5,000" }}
    </button>

    <div *ngIf="result()">
      <h2>Payment Confirmed</h2>
      <p>{{ result()!.currency }} {{ result()!.amount }}</p>
      <p>Reference: {{ result()!.reference }}</p>
    </div>

    <div *ngIf="error()">
      <p>{{ error() }}</p>
    </div>
  ',
})
export class CheckoutComponent {
  private paystack = inject(PaystackService);

  verifying = signal(false);
  result = signal<VerifyResult | null>(null);
  error = signal<string | null>(null);

  async handlePay() {
    // After popup onSuccess:
    // var reference = transaction.reference;
    this.verifying.set(true);

    try {
      var reference = 'example_ref'; // from popup callback
      var verified = await this.paystack.verify(reference);

      if (verified.verified) {
        this.result.set(verified);
      } else {
        this.error.set('Payment could not be verified');
      }
    } catch (err) {
      this.error.set('Verification failed. Contact support.');
    } finally {
      this.verifying.set(false);
    }
  }
}

Verify on the Redirect Callback Page

For redirect checkout, create a callback component that reads the reference from the URL and verifies immediately:

// src/app/payment-callback/payment-callback.component.ts
import { Component, OnInit, inject, signal } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PaystackService, VerifyResult } from '../services/paystack.service';

@Component({
  selector: 'app-payment-callback',
  standalone: true,
  template: '
    <div *ngIf="loading()">
      <p>Verifying your payment...</p>
    </div>
    <div *ngIf="result()">
      <h1>Payment Successful</h1>
      <p>Amount: {{ result()!.currency }} {{ result()!.amount }}</p>
      <p>Reference: {{ result()!.reference }}</p>
      <p>Paid via: {{ result()!.channel }}</p>
    </div>
    <div *ngIf="error()">
      <h1>Verification Failed</h1>
      <p>{{ error() }}</p>
      <p>If you were charged, contact support.</p>
    </div>
  ',
})
export class PaymentCallbackComponent implements OnInit {
  private route = inject(ActivatedRoute);
  private paystack = inject(PaystackService);

  loading = signal(true);
  result = signal<VerifyResult | null>(null);
  error = signal<string | null>(null);

  async ngOnInit() {
    var params = this.route.snapshot.queryParams;
    var reference = params['reference'] || params['trxref'];

    if (!reference) {
      this.error.set('No payment reference found in URL');
      this.loading.set(false);
      return;
    }

    try {
      var verified = await this.paystack.verify(reference);
      this.result.set(verified);
    } catch (err) {
      this.error.set('Could not verify payment');
    } finally {
      this.loading.set(false);
    }
  }
}

Register the route:

// app.routes.ts
{ path: 'payment/callback', component: PaymentCallbackComponent }

Backend Verification Endpoint

Your backend endpoint calls the Paystack Verify API and validates the result:

// server.js (Express)
app.get('/api/verify', async function(req, res) {
  var reference = req.query.reference;

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

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

  var data = await response.json();
  var txn = data.data;

  // Look up expected amount from database
  // var order = await db.orders.findOne({ reference: reference });
  // if (txn.amount !== order.amountInKobo) {
  //   return res.status(400).json({ verified: false, reason: 'Amount mismatch' });
  // }

  if (data.status && 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 });
});

Always compare the verified amount against your database. A successful status alone does not guarantee the correct amount was paid.

Error Handling with HttpInterceptor

Use an Angular interceptor to handle common errors globally:

// src/app/interceptors/error.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';

export const errorInterceptor: HttpInterceptorFn = function(req, next) {
  return next(req).pipe(
    catchError(function(error) {
      if (error.status === 0) {
        // Network error
        console.error('Network error during payment verification');
      }
      if (error.status === 400) {
        // Verification failed
        console.error('Payment verification failed:', error.error);
      }
      return throwError(function() { return error; });
    })
  );
};

Register it in your app config:

// app.config.ts
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { errorInterceptor } from './interceptors/error.interceptor';

export const appConfig = {
  providers: [
    provideHttpClient(withInterceptors([errorInterceptor])),
  ],
};

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

  • ✓Angular runs in the browser. Verification must happen on your backend because it requires the Paystack secret key.
  • ✓After the popup onSuccess callback fires, call your backend verification endpoint. The popup callback alone is not reliable.
  • ✓For redirect checkout, use ActivatedRoute to read the reference from query parameters and verify in ngOnInit.
  • ✓Always validate the verified amount against your backend records. Check both status and amount before granting value.
  • ✓Use Angular signals or BehaviorSubject for verification state to keep the UI responsive during the verification call.
  • ✓Webhooks provide backup confirmation. Even if the Angular verification call fails, the webhook confirms the payment independently.

Frequently Asked Questions

Can I verify Paystack payments directly from Angular?
No. The Verify API requires your secret key. If you call it from Angular, the key is visible in browser DevTools. Always verify through your backend.
What if the verification call fails but the customer was charged?
Show a message like "We are confirming your payment. You will receive an email shortly." Your webhook handler will confirm the payment independently and fulfill the order.
Should I use HttpClient Observables or Promises for verification?
Either works. Promises with async/await are simpler for one-off calls like verification. Observables are better if you need cancellation, retry logic, or want to compose with other streams.
How do I prevent double verification calls?
Use the loading signal to disable the button during verification. You can also debounce in the service or track verified references in a Set to skip already-verified transactions.

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

How Paystack Accept Payments Actually Works Under the Hood

Most tutorials show you the API call and skip to "payment successful." This guide traces what actually happens between your server, Paystack, the card networks, the issuing bank, and the acquiring bank. Every step, every handshake, every place things can fail.

Read more

Paystack Invalid Key: Every Cause and Fix

The Paystack "Invalid key" error means the API key you sent does not match any active key on your Paystack account. This guide covers every cause and the exact fix for each one.

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.