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. Account Name Matching and Fuzzy Comparison
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 20, 2026

Account Name Matching and Fuzzy Comparison

Normalize both names to uppercase, remove punctuation, split into word tokens, sort alphabetically, and count how many tokens overlap. Require at least 2 matching tokens (typically first name and surname). For tighter verification, use Levenshtein distance to catch minor spelling differences. Never use exact string equality for name comparison in payment systems.

In this article

  1. 1. Why Exact String Matching Fails
  2. 2. Step 1: Normalize Both Names
  3. 3. Step 2: Token-Based Matching
  4. 4. Step 3: Levenshtein Distance for Spelling Variations
  5. 5. African Name Edge Cases
  6. 6. Confidence Scores and Decision Logic
  7. 7. The User Confirmation Step

Why Exact String Matching Fails

When you resolve an account number with Paystack, the bank returns the name on the account. When a user signs up on your platform, they type their own name. These two names almost never match exactly.

Real examples of the same person appearing differently:

  • Bank returns: "OKAFOR JOHN CHUKWUEMEKA"
  • User typed: "John Okafor"
  • Bank returns: "AKINWALE FOLASHADE M"
  • User typed: "Folashade Akinwale"
  • Bank returns: "ODHIAMBO BRIAN OTIENO"
  • User typed: "Brian Odhiambo"

If you compare these with === or even case-insensitive comparison, every single one fails. You would block every legitimate payout.

The differences come from several sources: name order conventions vary by bank, middle names may or may not be included, single-letter initials appear in some records, casing differs, and some banks truncate long names.

Step 1: Normalize Both Names

Before comparing, bring both names into the same format.

// normalize.js
function normalizeName(name) {
  return name
    .toUpperCase()                    // Uniform casing
    .replace(/[^A-Z\s]/g, '')         // Remove punctuation, numbers, special chars
    .replace(/\s+/g, ' ')             // Collapse multiple spaces
    .trim()
    .split(' ')
    .filter(function(part) {
      return part.length > 1;         // Remove single-letter initials like "M" or "A"
    })
    .sort();                          // Alphabetical order removes position dependency
}

// "OKAFOR JOHN CHUKWUEMEKA" => ["CHUKWUEMEKA", "JOHN", "OKAFOR"]
// "John Okafor"              => ["JOHN", "OKAFOR"]

Why remove single characters: Banks often include middle initials ("M", "A", "O") that users never type. Keeping them would reduce match accuracy.

Why sort alphabetically: This eliminates the first-name-vs-surname order problem entirely. Whether the bank says "OKAFOR JOHN" or the user says "John Okafor," after sorting both become ["JOHN", "OKAFOR"].

Why uppercase: Case differences are trivial but would cause mismatches without normalization. Always normalize to one case.

Get weekly developer tips

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

No spam. Unsubscribe anytime.

Step 2: Token-Based Matching

After normalization, count how many name tokens (words) appear in both names.

// token-match.js
function tokenMatch(name1, name2) {
  var tokens1 = normalizeName(name1);
  var tokens2 = normalizeName(name2);

  var matches = 0;
  tokens1.forEach(function(token) {
    if (tokens2.indexOf(token) !== -1) {
      matches++;
    }
  });

  var minTokens = Math.min(tokens1.length, tokens2.length);
  var maxTokens = Math.max(tokens1.length, tokens2.length);

  return {
    matchCount: matches,
    minTokens: minTokens,
    maxTokens: maxTokens,
    ratio: maxTokens > 0 ? matches / maxTokens : 0,
    passes: matches >= Math.min(2, minTokens),
  };
}

// Examples:
// tokenMatch("OKAFOR JOHN CHUKWUEMEKA", "John Okafor")
//   => { matchCount: 2, minTokens: 2, maxTokens: 3, ratio: 0.67, passes: true }
//   JOHN and OKAFOR match. CHUKWUEMEKA has no match but 2 out of 2 user tokens matched.

// tokenMatch("AKINWALE FOLASHADE", "Bola Adeyemi")
//   => { matchCount: 0, minTokens: 2, maxTokens: 2, ratio: 0, passes: false }
//   No tokens match. Different person.

Threshold: at least 2 matching tokens. This requires both the first name and surname to match. If the user only has a single-word name (some people do), require 1 match.

Why not require all tokens to match: The bank often includes middle names that the user omitted. Requiring all tokens to match would reject users who simply did not type their middle name.

Step 3: Levenshtein Distance for Spelling Variations

Token matching fails when the same name is spelled slightly differently. "Chukwuemeka" and "CHUKWUEMKA" (missing an "e") are obviously the same name, but they are different strings.

Levenshtein distance measures how many single-character edits (insertions, deletions, substitutions) it takes to turn one string into another.

// levenshtein.js
function levenshtein(a, b) {
  var matrix = [];

  for (var i = 0; i <= b.length; i++) {
    matrix[i] = [i];
  }
  for (var j = 0; j <= a.length; j++) {
    matrix[0][j] = j;
  }

  for (var i = 1; i <= b.length; i++) {
    for (var j = 1; j <= a.length; j++) {
      if (b.charAt(i - 1) === a.charAt(j - 1)) {
        matrix[i][j] = matrix[i - 1][j - 1];
      } else {
        matrix[i][j] = Math.min(
          matrix[i - 1][j - 1] + 1,  // substitution
          matrix[i][j - 1] + 1,      // insertion
          matrix[i - 1][j] + 1       // deletion
        );
      }
    }
  }

  return matrix[b.length][a.length];
}

function fuzzyTokenMatch(token1, token2) {
  var distance = levenshtein(token1, token2);
  var maxLen = Math.max(token1.length, token2.length);
  var similarity = 1 - (distance / maxLen);
  return similarity >= 0.8; // 80% similarity threshold
}

// "CHUKWUEMEKA" vs "CHUKWUEMKA" => distance 1, similarity 0.91 => match
// "JOHN" vs "JOAN" => distance 1, similarity 0.75 => no match

Combine with token matching. Use Levenshtein as a fallback when exact token matching fails. If a token from name1 does not exactly match any token in name2, check if it fuzzy-matches (80%+ similarity) any token in name2.

// enhanced-match.js
function enhancedTokenMatch(name1, name2) {
  var tokens1 = normalizeName(name1);
  var tokens2 = normalizeName(name2);

  var matches = 0;
  tokens1.forEach(function(t1) {
    var exactMatch = tokens2.indexOf(t1) !== -1;
    if (exactMatch) {
      matches++;
      return;
    }
    // Try fuzzy match
    var fuzzyMatch = tokens2.some(function(t2) {
      return fuzzyTokenMatch(t1, t2);
    });
    if (fuzzyMatch) {
      matches++;
    }
  });

  return {
    matchCount: matches,
    passes: matches >= Math.min(2, Math.min(tokens1.length, tokens2.length)),
  };
}

African Name Edge Cases

African names have patterns that generic name-matching libraries do not handle well. Build your matcher with these in mind.

Compound surnames. Names like "Okonkwo-Eze" or "Abubakar-Sadiq" may appear with or without hyphens. Split on hyphens during normalization so "OKONKWO-EZE" becomes ["EZE", "OKONKWO"].

Prefix variations. "Oluwatobi" and "Tobi" can refer to the same person (the "Oluwa" prefix means "God" in Yoruba and is sometimes dropped in informal use). Your matcher cannot know this. This is why user confirmation is essential.

Married names. A woman might register on your platform with her married name but her bank account still shows her maiden name. Token matching will fail. The user confirmation step catches this.

Transliteration differences. "Mohammed", "Muhammad", "Muhammed" are all the same name. Levenshtein distance handles this reasonably well since the edit distance is small.

Single names. Some people legally have only one name. If the bank returns a single token and the user provides a single token, require just 1 match. Do not reject a user because they "should have" a first and last name.

Title prefixes. Some bank records include "Chief", "Dr", "Alhaji", or "Mrs". Strip common titles during normalization.

// strip-titles.js
var TITLES = ['CHIEF', 'DR', 'MR', 'MRS', 'MS', 'ALHAJI', 'ALHAJA', 'ENGR', 'PROF', 'PASTOR', 'ELDER'];

function stripTitles(tokens) {
  return tokens.filter(function(token) {
    return TITLES.indexOf(token) === -1;
  });
}

Confidence Scores and Decision Logic

Rather than a binary pass/fail, assign a confidence score and make decisions based on thresholds.

// confidence.js
function calculateNameConfidence(bankName, userName) {
  var bankTokens = stripTitles(normalizeName(bankName));
  var userTokens = stripTitles(normalizeName(userName));

  if (bankTokens.length === 0 || userTokens.length === 0) {
    return { score: 0, action: 'reject' };
  }

  var exactMatches = 0;
  var fuzzyMatches = 0;

  userTokens.forEach(function(ut) {
    if (bankTokens.indexOf(ut) !== -1) {
      exactMatches++;
    } else if (bankTokens.some(function(bt) { return fuzzyTokenMatch(ut, bt); })) {
      fuzzyMatches++;
    }
  });

  var totalMatches = exactMatches + (fuzzyMatches * 0.8);
  var score = totalMatches / Math.max(bankTokens.length, userTokens.length);

  var action;
  if (score >= 0.6) {
    action = 'auto_approve';
  } else if (score >= 0.3) {
    action = 'manual_review';
  } else {
    action = 'reject';
  }

  return { score: score, action: action, exactMatches: exactMatches, fuzzyMatches: fuzzyMatches };
}

// "OKAFOR JOHN CHUKWUEMEKA" vs "John Okafor" => score ~0.67 => auto_approve
// "OKAFOR JOHN CHUKWUEMEKA" vs "Jane Okafor" => score ~0.33 => manual_review
// "OKAFOR JOHN CHUKWUEMEKA" vs "Bola Adeyemi" => score 0 => reject

Three-tier decision:

  • Auto-approve (score >= 0.6): At least 2 tokens match. Proceed automatically.
  • Manual review (score 0.3 to 0.6): Partial match. Show the user both names and ask for confirmation. If the user confirms, proceed.
  • Reject (score < 0.3): Names are completely different. Block the payout and ask the user to re-enter their bank details.

The User Confirmation Step

Automated matching handles most cases, but it cannot catch everything. Always include a user confirmation step in your payout flow.

Show the resolved name clearly. After resolving the account, display: "This account is registered to: OKAFOR JOHN CHUKWUEMEKA. Is this your account?" with clear Yes and No buttons.

Log the confirmation. When the user clicks Yes, record it. This is your evidence if a dispute arises later. "The user confirmed the resolved name on [date] at [time]."

Handle "No" gracefully. If the user says the name is not theirs, let them re-enter their bank details. Do not lock them out. They may have simply selected the wrong bank or mistyped a digit.

The combination of automated fuzzy matching plus user confirmation gives you both speed (most matches resolve automatically) and safety (edge cases get human verification).

Key Takeaways

  • ✓Banks format names differently. Some use SURNAME FIRSTNAME, others use FIRSTNAME SURNAME. Some include middle names, some do not. Exact comparison fails constantly.
  • ✓Token-based matching (splitting names into words and counting overlaps) is the most reliable approach for African name formats.
  • ✓Normalize both names: uppercase, strip punctuation, remove single-character initials, split on whitespace, sort alphabetically.
  • ✓Require at least 2 matching tokens for a pass. This covers first name and surname regardless of order.
  • ✓Levenshtein distance catches minor spelling variations like "Chukwuemeka" vs "CHUKWUEMKA" (missing letter).
  • ✓Always show the resolved name to the user for manual confirmation, even when automated matching passes. This creates an audit trail.

Frequently Asked Questions

What matching threshold should I use for payouts?
Start with a 2-token minimum match for auto-approval. This means the first name and surname must both appear in the resolved name. Partial matches (1 token) should trigger manual review or user confirmation. Zero matches should block the payout pending re-entry of bank details.
Should I use a library for fuzzy matching instead of building my own?
Libraries like fuzzball or string-similarity work for general text matching, but they are not optimized for African name formats. Token-based matching with title stripping and Levenshtein fallback, as described in this guide, handles the specific edge cases you will encounter with bank-returned names. You can use a library for the Levenshtein calculation itself.
What if the bank returns an empty or garbled name?
Some banks return empty strings or placeholder text for certain account types (e.g., corporate accounts). If the resolved name is empty or clearly not a real name, skip automated matching and require manual confirmation from the user. Log the anomaly for your records.
How do I handle corporate or business account names?
Business accounts return the company name, not a personal name. Token matching against a person's name will always fail. Detect business accounts by checking for keywords like "LTD", "LIMITED", "PLC", "ENTERPRISES", "COMPANY" in the resolved name. For business payouts, match against the registered business name instead of the personal name.
Can name matching be fooled by a fraudster who knows the account holder name?
Yes. If a fraudster knows the name on a bank account, they can enter that name on your platform and pass the name match. Name matching prevents accidental errors (wrong bank, mistyped digits) but does not stop intentional fraud. For stronger protection, combine name matching with BVN verification and payout limits.

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 Identity Verification: Complete Guide

Paystack gives you three identity endpoints: resolve account number, resolve card BIN, and validate customer (BVN). This guide shows you how to use each one, build a KYC flow around them, and stay within the Kenya DPA and Nigeria NDPR while doing it.

Read more

Validating a Customer Before Payout

Every payout you send is money you cannot get back. Validating the recipient before initiating a transfer is not optional. This guide shows you how to chain Paystack identity APIs into a validation gate that catches wrong accounts, name mismatches, and suspicious requests before a single kobo leaves your account.

Read more

Building a KYC Flow on Paystack Identity APIs

A KYC flow is not a single API call. It is a sequence of verification steps that increase in strength as the user needs more from your product. This guide shows you how to chain Paystack identity APIs into a progressive KYC system with tiers, database tracking, and clear UX at every step.

Read more

Rate Limits and Caching for Identity Lookups

Paystack rate-limits identity endpoints to prevent abuse. If you call Resolve Account Number on every page load or every form submission, you will hit the limit and your verifications will fail. This guide shows you how to cache results properly, throttle requests, and build an identity lookup layer that stays fast even under heavy traffic.

Read more

Nigeria Data Protection Act and Payment Data Storage

Nigeria's Data Protection Act 2023 (NDPA) applies to any business that processes personal data of Nigerian residents — including payment data from Paystack transactions. This guide explains your obligations under the NDPA when storing customer payment records.

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.