Welcome coupons: a first-order discount for first orders only
A coupon like WELCOME10 is checked against the email, and a new email is free. At checkout, ask SpoofLens whether this browser has already ordered under other emails, and keep the order while you refuse the discount.
The problem
Coupon farming looks like many small first orders: new emails, the same delivery area, the same few browsers. Each one costs you the discount, and often a delivery subsidy too.
Blocking the order loses a sale. This recipe refuses only the welcome discount and says so before payment, so a returning customer can still buy at the regular price.
What the API gives you
seen.accounts_on_device_30d | How many of your customer accounts (here: billing emails) used this device in the last 30 days, this one included. |
device.id | Store it with every order. Your own orders table then answers a second question: has this device already used a welcome coupon, even more than 30 days ago? |
decision.action | What your rules say. An automated browser at checkout is rarely a customer. |
The code
It calls the helper (sl_check_visitor and sl_user_ref), which sends the browser data from the form, the IP address, the headers and a hash of the email.
PHP
<?php
// checkout handler, only when a welcome coupon is applied
$welcome = ['WELCOME10']; // your first-order coupon codes
$couponRefused = false;
$deviceId = '';
if (in_array(strtoupper($coupon), $welcome, true)) {
$r = sl_check_visitor(sl_user_ref($_POST['email']));
if ($r) {
$deviceId = $r['device']['id']; // save it with the order
$accounts = $r['seen']['accounts_on_device_30d'] ?? 1;
// your orders table: SELECT 1 FROM orders WHERE device_id = ? AND coupon IN ('WELCOME10') LIMIT 1
$usedBefore = welcomeCouponUsedOnDevice($deviceId);
$couponRefused = $accounts >= 2 || $usedBefore || ($r['decision']['action'] ?? '') === 'block';
}
}
if ($couponRefused) {
// back to the checkout before payment, never a silent change of the total:
// "The welcome discount is for a first order. You can still order at the regular price."
} Node.js
// checkout handler (Express), only when a welcome coupon is applied
const welcome = ['WELCOME10']; // your first-order coupon codes
let couponRefused = false;
let deviceId = '';
if (welcome.includes(String(coupon).toUpperCase())) {
const r = await slCheckVisitor(req, slUserRef(req.body.email));
if (r) {
deviceId = r.device.id; // save it with the order
const accounts = (r.seen && r.seen.accounts_on_device_30d) || 1;
// your orders table: SELECT 1 FROM orders WHERE device_id = ? AND coupon IN ('WELCOME10') LIMIT 1
const usedBefore = await welcomeCouponUsedOnDevice(deviceId);
couponRefused = accounts >= 2 || usedBefore || (r.decision && r.decision.action) === 'block';
}
}
if (couponRefused) {
// back to the checkout before payment, never a silent change of the total:
// "The welcome discount is for a first order. You can still order at the regular price."
} Python
# checkout handler (Flask), only when a welcome coupon is applied
welcome = {'WELCOME10'} # your first-order coupon codes
coupon_refused = False
device_id = ''
if coupon.upper() in welcome:
r = sl_check_visitor(request, sl_user_ref(request.form['email']))
if r:
device_id = r['device']['id'] # save it with the order
accounts = (r.get('seen') or {}).get('accounts_on_device_30d', 1)
# your orders table: SELECT 1 FROM orders WHERE device_id = ? AND coupon IN ('WELCOME10') LIMIT 1
used_before = welcome_coupon_used_on_device(device_id)
coupon_refused = accounts >= 2 or used_before or (r.get('decision') or {}).get('action') == 'block'
if coupon_refused:
# back to the checkout before payment, never a silent change of the total:
# "The welcome discount is for a first order. You can still order at the regular price."
pass Check only orders that carry a welcome coupon: an ordinary order does not need it, and every check counts toward your monthly quota.
Soft steps instead of a ban
- Refuse the discount, keep the order, and say so before payment. Never change the total silently.
- Offer a smaller returning-customer discount instead of a flat no, if your margins allow it.
- For block: hold the order for a manual look rather than cancelling it.
Rules in your account
Set them in Rules and alerts: the result comes back as decision in every answer, so your code does not change when the rules do.
- Start with the default thresholds: review below 70, block below 30, automated browsers blocked.
- Put the devices of known coupon farmers on the block list.
- Put your own team's devices on the allow list, so test orders never lose the coupon.
Where it can be wrong
- Two people in one household ordering for themselves is normal. If that matters for your shop, refuse the coupon only from the third account on one device.
- A VPN or a privacy browser on its own is not abuse: do not refuse a coupon for that alone.
One line for your privacy policy
To prevent abuse of discounts, we check technical characteristics of your browser with SpoofLens (spooflens.com) when you place an order with a coupon.
Limits
- The device ID belongs to one browser profile: the same person in another browser looks like a new device.
- The account count covers 30 days; the device ID in your orders table has no such limit.
- On WooCommerce our plugin already writes the number of accounts on the device into each order note, so coupon farming is visible today. It does not refuse coupons by itself: that needs code like the above.
Other recipes
- Free trial abuse: one trial per person, not per email
- Referral programs: no bonus for inviting yourself
- The API documentation
Get a free key 1,000 checks a month free, no card.