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

Verify Paystack Payments in Svelte and SvelteKit

To verify Paystack payments in SvelteKit, create a server endpoint that calls the Paystack Verify Transaction API with your secret key from $env/static/private. Call this endpoint from your Svelte component after the popup callback, or use a load function on the redirect callback page for SSR verification.

In this article

  1. 1. How Verification Works in SvelteKit
  2. 2. Verification Server Endpoint
  3. 3. Verify After the Popup
  4. 4. Verify on the Redirect Callback Page (SSR)
  5. 5. Amount Validation
  6. 6. Idempotent Fulfillment
  7. 7. Ship Payments Faster

How Verification Works in SvelteKit

After a customer completes payment, you need to confirm the transaction actually succeeded. The Paystack Verify Transaction API is the only reliable way. It requires your secret key, so it must run on the server.

SvelteKit gives you two server-side options:

  • Server endpoint (+server.ts): Create a GET endpoint that your Svelte component calls via fetch after the popup callback.
  • Load function (+page.server.ts): Verify during SSR on the redirect callback page. The result is available before the page renders.

Both approaches are valid. Use the endpoint for popup checkout and the load function for redirect checkout.

Verification Server Endpoint

// src/routes/api/verify/+server.ts
import { PAYSTACK_SECRET_KEY } from '$env/static/private';
import { json, error } from '@sveltejs/kit';

export async function GET({ url }) {
  var reference = url.searchParams.get('reference');

  if (!reference) {
    throw error(400, 'Reference is required');
  }

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

  var data = await response.json();

  if (!data.status) {
    throw error(400, data.message || 'Verification failed');
  }

  var txn = data.data;

  // Validate amount against database
  // var order = await db.orders.findOne({ reference: reference });
  // if (!order || txn.amount !== order.amountInKobo) {
  //   throw error(400, 'Amount mismatch');
  // }

  if (txn.status === 'success') {
    // Mark order as paid (idempotent)
    // await db.orders.updateOne({ reference, status: 'pending' }, { status: 'paid' });

    return json({
      verified: true,
      amount: txn.amount / 100,
      currency: txn.currency,
      reference: txn.reference,
      channel: txn.channel,
      paidAt: txn.paid_at,
    });
  }

  throw error(400, 'Payment status: ' + txn.status);
}

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 page -->
<script>
  var verifyState = 'idle'; // idle | verifying | success | failed
  var verifyResult = null;

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

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

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

{#if verifyState === 'verifying'}
  <p>Verifying your payment...</p>
{:else if verifyState === 'success'}
  <h2>Payment Confirmed</h2>
  <p>{verifyResult.currency} {verifyResult.amount}</p>
  <p>Reference: {verifyResult.reference}</p>
{:else if verifyState === 'failed'}
  <p>Verification failed. Contact support.</p>
{/if}

Verify on the Redirect Callback Page (SSR)

For redirect checkout, use a load function to verify during SSR:

// src/routes/payment/callback/+page.server.ts
import { PAYSTACK_SECRET_KEY } from '$env/static/private';

export async function load({ url }) {
  var reference = url.searchParams.get('reference') || url.searchParams.get('trxref');

  if (!reference) {
    return { verified: false, error: 'No reference found in URL' };
  }

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

    var data = await response.json();

    if (data.status && data.data.status === 'success') {
      // Update database (idempotent)
      return {
        verified: true,
        amount: data.data.amount / 100,
        currency: data.data.currency,
        reference: data.data.reference,
        channel: data.data.channel,
      };
    }

    return { verified: false, error: 'Payment status: ' + (data.data?.status || 'unknown') };
  } catch (err) {
    return { verified: false, error: 'Verification request failed' };
  }
}
<!-- src/routes/payment/callback/+page.svelte -->
<script>
  export var data;
</script>

{#if data.verified}
  <h1>Payment Successful</h1>
  <p>Amount: {data.currency} {data.amount}</p>
  <p>Reference: {data.reference}</p>
  <p>Paid via: {data.channel}</p>
  <a href="/">Back to Home</a>
{:else}
  <h1>Verification Failed</h1>
  <p>{data.error}</p>
  <p>If you were charged, contact support.</p>
{/if}

Because the load function runs on the server during SSR, the HTML arrives with the verification result already rendered. No loading spinner needed.

Amount Validation

Checking status === "success" is not enough. An attacker could initialize a transaction for 1 NGN instead of 10,000 NGN. Your server must compare the verified amount against your database:

// In your verification logic
var txn = data.data;

// Look up the order
var order = await db.orders.findOne({ reference: reference });

if (!order) {
  throw error(404, 'Order not found');
}

if (txn.amount !== order.amountInKobo || txn.currency !== order.currency) {
  // Log potential fraud
  console.error('Amount mismatch', {
    expected: order.amountInKobo,
    received: txn.amount,
    reference: reference,
  });
  throw error(400, 'Amount mismatch');
}

// Amount matches, proceed with fulfillment

Idempotent Fulfillment

The popup verification, the redirect callback, and the webhook can all confirm the same payment. Make your fulfillment logic idempotent so processing the same transaction twice has no extra effect:

// Update only if not already paid
var result = await db.orders.updateOne(
  { reference: reference, status: 'pending' },
  { status: 'paid', paidAt: txn.paid_at }
);

if (result.modifiedCount === 0) {
  // Already fulfilled, just return success without re-granting
  return json({ verified: true, amount: txn.amount / 100 });
}

// First time confirming, grant access, send email, etc.

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

  • ✓SvelteKit server endpoints and load functions handle verification securely. The Paystack secret key stays in $env/static/private.
  • ✓Use a +server.ts endpoint for popup verification and a +page.server.ts load function for redirect callback verification.
  • ✓Load functions verify during SSR so the customer sees the result immediately on the callback page.
  • ✓Always validate the verified amount against your database. A successful status is necessary but not sufficient.
  • ✓Make fulfillment idempotent. Both popup verification and webhooks can confirm the same transaction.
  • ✓Return only sanitized fields from the verification response. Do not expose the full Paystack response.

Frequently Asked Questions

Should I use a server endpoint or a load function for verification?
Use a server endpoint (+server.ts) when verifying after the popup callback via fetch. Use a load function (+page.server.ts) when verifying on the redirect callback page for SSR. Both approaches are secure and keep the secret key on the server.
Can I verify Paystack payments from a Svelte component?
Not directly. The Verify API requires your secret key. Your Svelte component calls your SvelteKit server endpoint, which calls Paystack. The component never touches the Paystack API directly.
What if the customer closes the browser after paying?
The verification call from your frontend will never fire. This is why webhooks are essential. Your webhook handler on the server receives the charge.success event independently and can fulfill the order.
How many times can I call the Paystack Verify API?
As many times as you need. The API is read-only and idempotent. It always returns the current state of the transaction. Paystack does not charge for verification calls.

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 Svelte and SvelteKit

Accept Paystack payments in SvelteKit using server endpoints for backend logic and Svelte components for the frontend. This guide covers initializing transactions, inline popup checkout, redirect checkout, and verification in a single full-stack SvelteKit project.

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

Migrating from Paystack InlineJS V1 to V2

Paystack Inline.js v2 changes the script URL, the initialization pattern, and how callbacks work. This guide walks through every breaking change, shows you how to update your code, and flags the subtle differences that cause silent failures if you miss them.

Read more

Paystack Webhook Returning 400 in Next.js App Router

Next.js App Router route handlers receive the request body as a ReadableStream. If you try to read it as JSON directly or use the wrong method, your Paystack webhook handler returns 400. This guide shows the complete working pattern for App Router webhook handlers.

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.