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. Retail Checkout with Paystack Terminal and Barcode Scanning
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Retail Checkout with Paystack Terminal and Barcode Scanning

Scan a product barcode to look up price and SKU from your inventory API. Build an order object with line items. Call POST /terminal/:terminal_id/event to send a purchase event to the Paystack Terminal. The terminal displays the amount and waits for card tap or PIN. When payment completes, Paystack fires a terminalaction.process.completed event — handle it to mark the sale as complete and print a receipt.

In this article

  1. 1. POS Architecture
  2. 2. Push Invoice to Terminal
  3. 3. Webhook: Mark Sale Complete
  4. 4. Learn More

POS Architecture

The retail checkout flow with Paystack Terminal:

  1. Cashier scans product barcodes → your POS app builds a cart
  2. Cashier hits "Charge" → your backend calls POST /terminal/:id/event
  3. Terminal displays amount → customer taps card or enters PIN
  4. Terminal sends result → your backend receives terminalaction.process.completed webhook
  5. Receipt printed, inventory updated

Your POS app (web or React Native) talks to your backend, which talks to Paystack. The terminal never talks to your app directly.

Push Invoice to Terminal

// POST /api/checkout — your POS backend endpoint
app.post('/api/checkout', async (req, res) => {
  var { terminalId, items } = req.body;

  // items: [{ name, sku, quantity, unit_price }]
  var total = items.reduce((sum, item) => sum + item.quantity * item.unit_price, 0);

  // Push payment to terminal
  var response = await fetch(
    'https://api.paystack.co/terminal/' + terminalId + '/event',
    {
      method: 'POST',
      headers: {
        Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        type: 'purchase',
        action: {
          amount: Math.round(total * 100), // kobo
          currency: 'NGN',
          reference: 'pos_' + Date.now(),
          description: items.map(i => i.name).join(', '),
        }
      }),
    }
  );

  var data = await response.json();

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

  // Store the sale in DB with status: 'awaiting_payment'
  var sale = await db.sales.create({
    reference: data.data.reference || ('pos_' + Date.now()),
    terminal_id: terminalId,
    items,
    total,
    status: 'awaiting_payment',
  });

  res.json({ sale_id: sale.id, total });
});

Get weekly developer tips

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

No spam. Unsubscribe anytime.

Webhook: Mark Sale Complete

// Handle terminal payment completion
if (event.event === 'terminalaction.process.completed') {
  var action = event.data;

  if (action.status === 'success') {
    await db.sales.update(
      {
        status: 'paid',
        paid_at: new Date().toISOString(),
        terminal_reference: action.reference,
      },
      { where: { reference: action.reference } }
    );

    // Update inventory
    var sale = await db.sales.findByReference(action.reference);
    for (var item of sale.items) {
      await db.inventory.decrement({ sku: item.sku, by: item.quantity });
    }

    // Trigger receipt print via your receipt printer API
    await printReceipt(sale);
  }
}

Learn More

This guide is part of the Paystack Terminal guide series.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Use POST /terminal/:terminal_id/event with type: "purchase" and action.amount to push a payment request to a terminal.
  • ✓Barcode scanning is done in your app — scan, look up price, build cart, then push total to the terminal.
  • ✓The terminal fires a terminalaction.process.completed event when the customer completes payment.
  • ✓Build an inventory lookup API that maps barcode → { name, price, sku }. This is your POS product catalog.
  • ✓Each Terminal has a unique terminal_id (TRM_xxxx). Store terminal IDs against physical devices in your database.
  • ✓Handle the webhook in under 500ms — the terminal waits for your acknowledgment before showing success to the cashier.

Frequently Asked Questions

How do I get the terminal_id for a specific physical device?
Call GET /terminal in the Paystack API to list all terminals on your account. Each device returns a terminal_id (TRM_xxxx), a serial_number, and a device_name. Store these in your database and let cashiers select which terminal to charge from your POS interface.
Can I send line items to the Paystack Terminal display?
The Terminal API purchase event accepts amount, currency, reference, and description. The description field can contain a summary of items. The terminal displays the total amount prominently. For a detailed receipt with line items, generate the receipt on your side after payment completes.
What if the customer does not complete payment on the terminal?
The terminal action will time out and Paystack fires terminalaction.process.completed with status: "timeout" or "cancelled". Handle this by marking the sale as cancelled and resetting the POS cart. You can also call POST /terminal/:id/event with type: "print" to clear the terminal screen.
Can I use any barcode scanner with Paystack Terminal?
Paystack Terminal does not include a built-in barcode scanner. Use a separate USB or Bluetooth barcode scanner connected to the cashier's POS device (tablet or computer). Your POS app reads the scan input and looks up the product. The terminal only handles the payment step.

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 Terminal and In-Person Payments: Complete Guide

Paystack Terminal lets you accept card payments at a physical location using Paystack POS devices. You push a payment request from your server to the terminal, the customer taps or inserts their card, and you get a webhook when it succeeds. This guide covers the full integration.

Read more

Terminal Invoice Payments via API

When a customer walks in with an invoice to pay, your system needs to push the exact invoice amount to the terminal, track which invoice the payment covers, and mark it as settled once the card payment completes. This guide covers the full flow: from invoice lookup to terminal push to webhook reconciliation.

Read more

Building a Terminal App with React Native

If your team already knows React, React Native is the fastest path to a native POS app. This guide shows you how to structure a React Native terminal app, communicate with your backend, manage payment state, and design a tablet-first interface that cashiers can operate under real-world conditions.

Read more

Restaurant Ordering and Terminal Payment Flow

Restaurant payment flows are different from retail. Orders change after they are placed (added dessert, extra drinks). Bills split between customers. Tips are added after the meal. This guide shows you how to build a restaurant ordering system that works with Paystack Terminal for the final payment step.

Read more

Paystack Refunds: Full, Partial and Programmatic

Refunds are unavoidable in any payment system. Paystack gives you a Refund API to process full refunds, partial refunds, and automated refund workflows. This guide covers the conditions, timelines, webhook events, and working Node.js code for building a refund handler.

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.