# Checkout rules

> Decide allow, review or block for every checkout with your own logic, next to ShopFast's fraud checks.

Source: https://shopfa.st/docs/plugins/checkout

Checkout rules run as a risk check before every checkout, with the store's blocklists and velocity limits. The
strictest verdict wins.

```lua
function plugin.validate_cart(ctx, input)
  local cart = input.cart
  local limit = tonumber(ctx.settings.review_above) or 20000
  if cart.totalMinor > limit and not cart.signedIn then
    return { action = "review", reason = "guest order over " .. money.format(limit, cart.currency), points = 30 }
  end
  if cart.country ~= cart.ipCountry and cart.proxy then
    return { action = "block", message = "We could not confirm this order. Contact the store.", reason = "proxy and country mismatch", points = 60 }
  end
  return { action = "allow" }
end
```

`input.cart`: `productIds`, `totalMinor`, `currency`, `emailDomain`, `country` (the buyer's choice), `ipCountry`,
`proxy` (VPN or hosting network) and `signedIn`. Plugins never see full emails, card details or the IP address.

| `action` | Effect |
| --- | --- |
| `allow` | Nothing (add `points` and a `reason` to log a signal anyway) |
| `review` | The buyer confirms their email with a code before paying, and the check is flagged |
| `block` | The checkout stops with your `message` (keep it neutral: never say which rule matched) |

Every verdict is logged in **Settings → Security & Fraud** with the plugin's name and your `reason`.

Checkout rules **fail open**: a plugin that errors or runs out of budget is skipped (and its run is marked failed), so
a bug never stops every sale. They get the smallest budget (2M instructions, 200 ms CPU, one HTTP request) because the
buyer is waiting. Plugins decide; they cannot change prices or the cart.
