CardFence

How it works

Why WooCommerce stores get card-tested

A card tester holds thousands of stolen card numbers and needs to know which still work. Small stores make good test targets: checkout is open to guests and fraud screening is light. A bot places tiny orders over and over, each with a different card. Each attempt can:

  • cost you an authorization fee
  • create a failed or pending order
  • send a "Failed order" email that bounces

Enough declines can get your payment account reviewed or frozen.

Many security and CAPTCHA plugins only watch the classic checkout. The Cart and Checkout blocks, the default in WooCommerce since 8.3, take payment through the Store API instead. That is a separate route, and bots use it directly. CardFence covers both, and every other way into your gateway.

What is protected

Route Examples Checks applied
Block checkout POST /wp-json/wc/store/v1/checkout, the unversioned /wc/store/checkout, ?rest_route= URLs, sub-requests inside /wc/store/v1/batch All
Pay for order (blocks) POST /wc/store/v1/checkout/{order id} All
Classic checkout The [woocommerce_checkout] form (AJAX or not) All
Pay for order (classic) The order-pay page All
Add payment method My Account → Payment methods → Add All. A favorite of card testers because it creates no order
Gateway AJAX endpoints ?wc-ajax=ppc-create-order, ppc-approve-order (PayPal Payments) and any you add Rate limits and blocks only

Requests are checked before WooCommerce calls the payment gateway. For the block checkout, they're checked before an order is even created. A second check sits right before the gateway runs, in case some other code path ever reaches payment without passing the first.

The checks, in order

The first rule that applies decides.

  1. Allow list. IPs and ranges you trust go straight through.
  2. Block lists. IPs, ranges, emails and email domains you blocked are refused.
  3. Store managers. Logged-in users who can manage WooCommerce are never limited.
  4. Pro rules. Disposable email addresses and blocked countries, if you use Pro and switched them on.
  5. Temporary blocks. An IP or email blocked for too many attempts or declines is refused until the block lifts.
  6. Trusted customers. Logged-in customers who have paid before go through.
  7. Rate limit. If this IP already made the maximum number of checkout attempts in the window, the attempt is refused and the IP is blocked for a while. The limit halves during attack mode.
  8. Browser check. No proof that a real browser opened your checkout page: logged, or refused when enforced.
  9. Turnstile. When the challenge is active, it must have been passed.

Anything that passes is logged as Allowed and counts towards the rate limit.

In Monitor only mode, nothing is ever refused. Each refusal is logged as Would block: … instead.

After the payment: failures

Declines are what card testing produces, so CardFence counts them. It picks them up from three places and counts each order only once:

  • an order moving to Failed, whatever the checkout or gateway;
  • the Store API answering with a payment error, for gateways that leave the order pending;
  • a classic checkout, order-pay or add-payment-method request that ran a payment and didn't succeed.

Each decline is attributed to the IP that made the checkout request. Some declines arrive later, for example from a gateway webhook. Those use the order's recorded customer IP, so the gateway's own servers are never blocked. Those late failures are only counted for orders placed through checkout in the last 24 hours. Subscription renewals and admin-created orders failing in bulk are never counted as an attack. Status changes made by store staff in wp-admin are ignored.

Then:

  • Too many declines from one IP: that IP is blocked (default: 5 per hour → 12-hour block).
  • Too many declines for one email: that email is blocked from any IP, which catches testers who rotate IPs.
  • A spike across the whole store: attack mode switches on (default: 10 declines within 15 minutes).

Attack mode

Attack mode adds friction only while it's needed. While it's on:

  • the rate limit per IP is halved;
  • the Turnstile challenge is required, if set to During attacks and configured;
  • the browser check is enforced, if set to During attacks;
  • WooCommerce's Failed order and Customer failed order emails are paused, protecting your email reputation;
  • the dashboard turns red, and a notice appears across wp-admin.

It switches itself off when its time runs out (60 minutes by default), and extends while failures continue. You get one email when it starts automatically, at most every 6 hours. With Pro you can also get a Slack or webhook alert. You can also switch it on by hand from the dashboard: for 1 hour, 6 hours, 24 hours or 3 days.

The browser check

On cart, checkout, order-pay and add-payment-method pages, CardFence sets a session cookie (cksh_sid) and embeds a short signed token tied to it. A real browser that loaded the page sends both back with the order. A script posting straight to the checkout API has neither. The token also records when the page loaded, so an order submitted faster than a human could fill the form (2 seconds by default) is caught too.

By default the browser check is Log only: missing tokens are recorded as Suspicious but never block anyone.

Express payment buttons

Apple Pay and Google Pay buttons are always rate-limited like any checkout. While the browser check or Turnstile is enforced, express buttons on product pages are refused, because those buttons can't carry a challenge. With the recommended settings that only happens during an attack. The buttons on the cart and checkout pages keep working.

Attack-order flagging

When a payment fails during attack mode, or from an IP or email that was then blocked for it, the order gets a flag (_cksh_suspect) and an order note: CardFence: flagged as a card-testing attempt. A real customer's one-off decline is never flagged. With Pro, flagged orders can be moved to the trash in one click or automatically. See Pro.