AI Build South Africa
Examples checked against PayFast's, Ozow's, Peach Payments' and Yoco's own signature documentation, September 2026.

Quick answer

A failed signature check means your server rebuilt the gateway's signature from the notification it received and got a different result. The payment and your account are usually fine. On any gateway, it almost always comes down to one of seven causes: the wrong or test secret, fields in the wrong order, empty fields left in, a tidied-up copy of the message, special characters, small differences that are easy to miss, or a time check that's too strict. Don't switch the check off. Match your pattern in the table below, then give your AI the prompt to find which cause it is.

What the signature check actually does

When a payment goes through, the gateway sends your server a notification (a webhook) saying which order was paid. Anyone on the internet could send your server a message that looks like that, so the gateway signs it. It runs the notification and a secret that only you and the gateway know through a fixed recipe, and attaches the result.

Your server runs the same recipe on what it received, with its copy of the secret, and compares the two results. If they match, the message really came from the gateway and nobody changed it on the way. If one character differs anywhere, the results are completely different. That's the failure you're seeing.

So a failed check rarely means the payment failed, your account is blocked or the gateway is down. It means your server and the gateway didn't follow exactly the same recipe, or didn't use the same secret. Some gateways also ask you to sign the checkout request you send them. The same causes apply in that direction, with the gateway doing the checking.

Why AI-written code gets this wrong so often

Every gateway has its own recipe: which fields go in, in what order, how they're written out, which mixing method (called a hash) combines them, and which secret is used. Many recipes differ in only one or two details. Your AI coding agent has seen code for dozens of gateways, and when it writes from memory it often mixes one gateway's recipe with another's. The code looks right, runs without an error, and fails every check.

Here's a real example. PayFast's payment and notification signatures keep the fields in the order they appear, unsorted. Peach Payments sorts its checkout fields alphabetically before signing. An AI that "tidies up" PayFast code by sorting the fields breaks every signature, and nothing on the screen tells you why. That's why the fix below starts with the gateway's own current documentation, not with the AI's memory.

Two kinds of recipe, and why it matters which is yours

Recipes come in two broad kinds. Knowing which one your gateway uses tells you where to look first.

  • The whole message, exactly as sent. The gateway signs the notification byte for byte, usually together with an ID and a time. Yoco's notifications and Peach Payments' notifications work this way. Here the usual failure is that your code checked a changed copy of the message (cause 4 below).
  • A line rebuilt from the payment fields. The gateway strings chosen fields together into one line of text and signs that. PayFast works this way, and so does Ozow's older Payments API. Here what matters most is the order of the fields, which ones are left out and how each value is written (causes 2, 3 and 5).

The usual causes, on any gateway

These are what almost every failed check comes down to, whichever gateway you use. The gateways named are only examples of each cause; your own gateway's documentation says which details apply to you.

1. The wrong secret, or the test one on your live site

Gateways give you separate secrets for the sandbox (test mode) and for live payments. A test secret left in your live settings fails every live notification, even though the code is fine. Some gateways also use one secret for signing checkout requests and a different one for checking notifications, and using one in place of the other won't match. Peach Payments, for example, can sign its notifications with a separate webhook secret. Some secrets also arrive with a label on the front that isn't part of the secret itself: Yoco's start with whsec_, which has to come off before use. And a space or line break copied in with the secret changes it, even though the extra character is invisible when you look at the value.

2. The fields in the wrong order

Some gateways sign the fields in the order they were sent, some sort them first, and some publish one fixed order that you must follow. Ozow's documentation for its older Payments API says the fields "must be concatenated in exactly the order listed". If the order is wrong, every check fails, from the very first payment.

3. Fields left in, or left out, when they shouldn't be

Some recipes skip empty fields and some include every field that was sent. Ozow's older Payments API says "empty or unused fields must be excluded". PayFast shows how easily this catches people out: when you sign a checkout it leaves blank fields out, but when you check its notification, every field it sent goes in. If your code includes a blank field the recipe skips, or drops one it needs, the results won't match.

4. A tidied-up copy of the message instead of the original

This one applies to the first kind of recipe, where the gateway signs the whole notification exactly as it was sent. Code often reads the message, turns it into data and then turns it back into text before checking it. That round trip can change spacing and the order of items while the content looks the same, and the check fails. The fix is to check the exact text that arrived, and only read it as data afterwards. Suspect this one when the check broke after the AI changed how your server reads incoming messages.

5. Special characters written out differently

Some recipes turn each value into web-safe text before signing, and programming languages differ slightly in how they do that. PayFast's recipe follows PHP's encoding method, which also converts characters such as ' and ( ). JavaScript's usual method leaves those alone. So a payment from a customer called O'Brien, or for a product with brackets in its name, fails the check while every other payment passes. If only some payments fail, suspect this first.

6. Small differences that are easy to miss

Upper case versus lower case, an amount written as 100 instead of 100.00, or a label left on the front of the signature. Ozow's Payments API documentation calls the amount's decimal formatting "the most common cause of a failed hash check". Yoco puts v1, in front of the signature it sends, and that has to come off before comparing. Each of these means the same to a person reading the value but is different to the recipe.

7. A time check that's too strict

Where the recipe includes the time the notification was sent, the checking code often also rejects notifications that are too old, so nobody can capture a real one and send it again later. If the server's clock is wrong, or the allowed window is set too short, genuine notifications fail too. This usually shows up as failures now and then, with nothing in common between the failing orders.

Match the pattern you're seeing

When the failure happens tells you more than the error does. Find your pattern, then start with the causes it points to.

What the pattern of failures points to
What you're seeingCheck first
Fails from the very first testThe secret (1), the field order (2) and which fields are included (3): the recipe itself doesn't match
Fails only since going liveThe secret (1): a test value is still in your live settings
Most payments pass, a few failSpecial characters (5) and amount formatting (6): look at what the failing orders have in common
A few fail, with no patternThe time check (7): the server's clock or the allowed window
Broke after the AI changed somethingThe tidied-up copy (4), or a "clean-up" that sorted or reformatted the fields (2, 6)
Your logs show nothing arriving at allNot a signature problem: see “When it isn't a signature problem at all” below

The prompt to give your AI

Paste this into your AI website builder's chat, or give it to your AI coding agent. Fill in the brackets. If your AI can't browse the web, paste in the gateway's documentation page on checking notifications yourself.

I'm not a coder. The payment notifications from [gateway name] are
failing the signature check on my server. What I'm seeing: [every
notification fails / only live fails / only some orders fail /
some fail now and then / it broke after a change].

Do not switch the check off, skip it or accept messages "in test mode".

1. Find [gateway name]'s current documentation for checking the
   signature on its notifications and give me the link.
2. Compare my code with it, point by point:
   - which secret it uses, and whether it's the live or the test one
   - which fields go into the signature, and in what order
   - whether empty fields are handled the way the documentation says
   - whether it checks the exact message as received, or a rebuilt copy
   - how values are encoded, including characters like ' and ( )
   - upper or lower case, amount formatting and any prefixes to remove
   - if the message includes a time, how old a notification may be
3. Tell me which difference you found, and why it causes the failure,
   before you change anything.
4. Add a log line that shows each notification arriving and whether
   its signature matched. Never print the secret itself.
5. Change only the signature-checking code. Don't touch anything else.

What to check in the AI's answer

  • It gave you a link to the gateway's own documentation. Open it. If the AI describes the recipe from memory, ask it to follow the documentation instead.
  • It named one specific difference. "The fields were sorted and the documentation says they aren't" is an answer. "Try this and see" is a guess, so ask it to compare again.
  • The check is still there and still rejects bad messages. It shouldn't be commented out, skipped in test mode, or logged and then ignored.
  • It compares the two signatures safely. Ask: "Do you compare the signatures with a timing-safe comparison?" That's a method that doesn't give clues away through how long the comparison takes. The answer should be yes.
  • The secret isn't in the browser code or the logs. Ask: "Which files run in the browser, and do any of them contain the secret?" The answer should be no.
  • Nothing else changed. Ask it to list the files it changed. It should be the signature-checking code only.

Test it with a real sandbox payment

A signature that matches when the AI works it out by hand doesn't prove the live code works. Run a full test payment through the gateway's sandbox and read your server's logs. You're looking for the line from step 4 of the prompt showing the notification arrived and its signature matched, not just a thank-you page. If some orders failed before, test with a name or product that has an apostrophe or brackets in it too.

When it isn't a signature problem at all

If your logs show no notification arriving, the gateway never reached your server, and no change to the signature code will help. The usual reasons: the notification address you gave the gateway still points at a test or local address, not your live site, or your server function only accepts signed-in users and turns the gateway away. Our guide to when an outside service gets the error covers that second case.

If this didn't fix it

Not affiliated with the platforms we write about

AI Build South Africa is an independent resource. We're not endorsed by, or an official partner of, PayFast, Ozow, Yoco or Peach Payments. We do participate in affiliate and referral programmes with some providers we write about. See our Disclaimer for the full disclosure and our Terms of Service for the wider terms.

Frequently asked questions

Why does the signature check work in the sandbox but fail on my live site?

Almost always because a test value is still in your live settings, usually the sandbox secret (some gateways call it a passphrase). The checking code shouldn't change between the two, only the values feeding it, so check the values before you change any code.

Why does my PayFast signature not match?

The same causes apply as on any other gateway, and on PayFast three come up most: the fields were sorted, whereas PayFast signs them in the order they were sent; blank fields were dropped from the notification check, whereas PayFast's notification check includes every field it sent; or a value with an apostrophe or brackets was encoded differently from the PHP method PayFast follows. A stray space copied in with your secret is the next thing to check.

What is a webhook secret, and why do I have more than one?

It's a value only you and the gateway know, used to sign and check notifications. It belongs on your server, never in your page's code. You get separate secrets for the sandbox and for live payments, and some gateways use one secret for checkout requests and another for notifications. Using the wrong one of any pair fails every check.

What does HMAC mean, and is verification the same as validation?

HMAC (hash-based message authentication code) is a common way of mixing a message and a secret into a signature. Without the secret, nobody can produce the right result. Signature verification and signature validation mean the same thing: your server recalculates the signature and compares it with the one that came with the message.

My webhook isn't arriving at all. Is that a signature problem?

No. If your server's logs show no notification arriving, the gateway never reached your server, so the signature code never ran. Check that the notification address you gave the gateway is your live site's address, and that your server function doesn't turn away callers who aren't signed in.

Can I use an online signature generator to test it?

Only with made-up test values. A generator can show whether your code produces the same signature as a known example. Never paste a live secret into any website. If a live secret has been exposed anywhere, replace it with a new one in the gateway's settings and on your server.

Is it safe to switch the signature check off until it works?

No. The check is what stops anyone who isn't the gateway from sending your server a fake "payment successful" message. A server that accepts any notification lets a stranger mark their own unpaid order as paid. Fix the mismatch instead, and test with sandbox payments while you do.

Related reading