PayFast says paid, WooCommerce order still pending
The shopper paid, PayFast shows the money, but the order in WooCommerce is still Pending payment or On hold. This almost always means one thing: PayFast's payment notification (the ITN, Instant Transaction Notification) never reached your store, or it reached it and your PayFast plugin rejected it.
WooCommerce does not learn about a PayFast payment from the shopper's browser. It learns from a server-to-server POST that PayFast sends to your store's notify URL. If that POST fails, the order never moves.
First: fix the order that is stuck
- Log in to PayFast and confirm the payment really completed (the amount and the order number in the item description).
- In WooCommerce, open the order, set the status to Processing (or Completed), and add a private note with the PayFast payment ID. Stock is reduced and emails go out as normal.
Then find the cause, or the next order gets stuck too.
Step 1: turn on your plugin's debug log
Most PayFast plugins for WooCommerce have a debug or log setting. Turn it on, place a small test order (sandbox mode is fine), then read WooCommerce > Status > Logs and pick the PayFast log for today.
- No log entry at all for the notification: the ITN never arrived. Go to step 2.
- An entry with an error: the ITN arrived and failed a security check. Go to step 3.
Step 2: the ITN never arrives
PayFast's own help centre lists the usual reasons:
- The notify URL is not reachable from the internet. Staging sites, local sites, sites behind HTTP basic auth or a "coming soon" page. PayFast also rejects return, cancel and notify URLs that contain "local" or "localhost".
- The notify URL redirects. A 301 or 302 (to a login page, from http to https, from www to non-www) loses the POST body. Test it yourself:
curl -i -X POST "https://yourstore.co.za/?wc-api=YOUR_GATEWAY_ID" -d "test=1"
You want a 200 or a plain error from the plugin, not a 301, 302, 403 or a firewall challenge page. The exact URL is in your gateway's settings or documentation.
- A firewall or security plugin blocks PayFast. Server firewalls (for example cPGuard on cPanel hosts), security plugins and Cloudflare bot rules can block the POST. Allow PayFast's IP ranges and ports listed in the PayFast developer docs.
- A non-standard port. PayFast only sends ITNs on ports 80, 8080, 8081 and 443.
PayFast retries a notification that does not get a 200 back: again immediately, then after 10 minutes, then at longer and longer intervals until it gives up. So once you fix the cause, recent stuck orders may update on their own. In the sandbox, notifications are sent only once, so a failed sandbox test will not retry.
Step 3: the ITN arrives but is rejected
The log message tells you which check failed:
| Log message | Cause | Fix |
|---|---|---|
| Invalid signature / signature mismatch | The salt passphrase in PayFast and in the plugin differ | See PayFast ITN invalid signature |
| Bad source IP / invalid source | Your site is behind Cloudflare or another proxy, so the request seems to come from the proxy | See PayFast bad source IP behind Cloudflare |
| Amount mismatch | The order total changed after checkout (a plugin edited the order, or currency rounding) | Check for plugins that change the order total after payment starts |
| Bad access of page / validation failed | Your server could not call PayFast back to confirm the data (outgoing HTTPS or cURL blocked) | Ask your host to allow outgoing HTTPS to www.payfast.co.za |
If you want a plugin that tells you the reason
We make a free PayFast gateway, Skillmerc Payment Gateway for PayFast. It logs every rejected ITN with the exact reason (signature, amount, source host, server confirmation), has a reverse proxy setting for Cloudflare, supports the checkout block and HPOS, and does refunds from the order screen. It is independent and not made by PayFast. The fixes above work with any PayFast plugin.