How to detect promo abuse

machine with multiple tickets coming out

Summarize this article with

What is promo abuse?

Promo abuse, or promotion abuse, occurs when individuals exploit a business's promotional offers for unauthorized discounts or advantages, leading to potential financial losses. Tactics typically include redeeming the same promotional code multiple times, stacking codes in unintended ways, creating multiple accounts to benefit from one-time offers, or using bots to seize high-value promotional items.

The business impact of promo abuse

While increased sales benefit your business, promo abuse can lead to financial losses from unwarranted discounts and deplete inventory intended for genuine customers. Preventing customers, either signed-in or anonymous, from abusing your promotions can help:

Prevent financial losses

Preventing promo abuse is essential to avoid financial losses from unauthorized discounts or fraudulent transactions. When these offers are exploited, they can negatively impact profits and potentially jeopardize the financial health of the business.

Increase sales

By curbing abuse, companies can direct promotions to their intended audience: genuine customers planning to purchase. This active approach enhances the effectiveness of sales campaigns and, subsequently, increases overall sales by concentrating resources on users who are most likely to buy products.

Boost legitimate customer experience

Preventing abuse ensures promotions and inventory remain accessible to real customers as intended. If abuse goes unchecked, it could lead to key items selling out or special offers becoming unavailable, creating a poor experience for legitimate customers.

Improve marketing analytics accuracy

Controlling promo abuse significantly improves the accuracy of marketing data, making it easier to extract meaningful insights. This reliable data on promotional engagement, customer behavior, and ROI assists companies in making better decisions and strategic plans.

How to detect promo code abuse

Install a browser identification service

Device identification services like Fingerprint can accurately identify anonymous visitor traffic by uniquely identifying their browsers. With JavaScript that runs in the background, Fingerprint generates a unique visitor identifier anytime someone performs a critical action, such as checking out. With this visitor ID, you can then monitor which visitors have already used or redeemed promotions and enforce limits on their use. This visitor ID remains stable even when cookies are cleared, VPNs are enabled, or in private browsing modes. This makes it a more reliable identifier than traditional methods, such as IP addresses.

Fingerprint identifies visitors on our servers rather than relying only on client-side code, which can be easily manipulated. Additionally, we offer server-side tools to validate incoming fingerprints, adhering to the best practice of never trusting data from the client side. With our visitor identification API, you can protect your storefront’s system against fraudsters without harming the checkout experience for legitimate customers.

Configuring Fingerprint for promo abuse prevention

Here's a basic example of how you can add logic for protecting promo codes; however, your use case may require additional checks.

First, you must incorporate the Fingerprint JavaScript agent into your store's front end to collect browser signals and generate a unique identifier. If your front end is built on a framework like Next.js or Vue, you can streamline the process using one of our open-source SDKs.

// Initialize the agent.
const fpPromise = import("https://fpjscdn.net/v3/<your-public-api-key>").then(
  (FingerprintJS) =>
    FingerprintJS.load({
      region: "us",
    })
);

``

For production deployments, we recommend routing requests to Fingerprint's APIs through your domain using the endpoint parameter. This prevents ad blockers from disrupting identification requests and improves accuracy. We offer a variety of ways to do this, which you can learn more about in our guide on how to protect your JavaScript agent from ad blockers.

When a customer attempts to use a promo code, make an identification request and send the requestId you receive from Fingerprint with the promo code.

async function onCheckout() {
  // Collect browser signals and request visitor identification
  // from the Fingerprint API. The response contains a `requestId`.
  const { requestId } = await (await fpPromise).get();

  const promoData = {
    promoCode,
    requestId,
  };

  const response = await fetch(`/api/checkout/promos`, {
    method: "POST",
    body: JSON.stringify(promoData),
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
    },
  });
}

The following steps should be performed on the backend using data provided by the Fingerprint Server API. If your backend logic is built on top of Node.js or any other popular server-side framework or language, you can use one of our Fingerprint Server API SDKs. Alternatively, you can also use the Webhooks functionality or Sealed Client Results for even lower latency.

Let's get the full identification event data using the  requestId by hitting the Server API /events endpoint. This example uses the Node SDK.

import {
  FingerprintJsServerApiClient,
  Region,
} from "@fingerprintjs/fingerprintjs-pro-server-api";

const fpServerApiClient = new FingerprintJsServerApiClient({
  apiKey: process.env.FP_SECRET_API_KEY,
  region: Region.Global,
});

// Get request ID and promo code from server request
const {requestId, promoCode} = request.body

const event = await fpServerApiClient.getEvent(requestId);
const visitorId = event.products.identification.data.visitorId;

The server API response must contain information about this specific identification request. If not, the request might have been tampered with, and we don't trust this identification attempt. Read our documentation to learn more about the checks you can use to prevent client-side tampering.

During the checkout process, such as when a button like "Apply Promo Code" is clicked, verify if the promo code exists and apply your established policies and rules. In the example below, we check if the promo code has already been claimed by the visitor before.

// Check if a promo exists with the given promo code.
export async function checkPromo(code) {
  return await PromoCode.findOne({
    where: {
      code: {
        [Op.eq]: code,
      },
    },
  });
}

// Check if the `visitorId` has used the promo code already.
async function getVisitorClaim(visitorId, code) {
  return await PromoClaim.findOne({
    where: { visitorId, code },
  });
}

const promo = await checkPromo(promoCode);

// Return an error if the promo doesn't exist.
if (!promo) {
  return getForbiddenResponse(
    res,
    "Provided promo code does not exist.",
    "error"
  );
}

const wasPromoClaimedByVisitor = await getVisitorClaim(
  visitorData.visitorId,
  promoCode
);

// Return an error if the visitor has already claimed the promo code.
if (wasPromoClaimedByVisitor) {
  return getForbiddenResponse(
    res,
    "You have already used this promo code.",
    "error"
  );
}

You can also check if the visitor claimed another promo recently. If everything is fine, we mark the promo as claimed in the database and adjust the price accordingly.

async function checkVisitorClaimedRecently(visitorId) {
  const oneHourBefore = new Date();
  oneHourBefore.setHours(oneHourBefore.getHours() - 1);

  return await PromoClaim.findOne({
    where: {
      visitorId,
      timestamp: {
        [Op.between]: [oneHourBefore, new Date()],
      },
    },
  });
}

// Claim the promo on behalf of the visitor.
export async function claimPromo(visitorId, code) {
  const claim = await PromoClaim.create({
    code,
    visitorId,
    timestamp: new Date(),
  });
  await claim.save();

  return claim;
}

const visitorClaimedAnotherPromoRecently = await checkVisitorClaimedRecently(
  visitorData.visitorId
);

if (visitorClaimedAnotherPromoRecently) {
  return getForbiddenResponse(
    res,
    "The visitor claimed another promo recently.",
    "error"
  );
}

await claimPromo(visitorData.visitorId, promoCode);

return getOkResponse(
  res,
  `Promo claimed successfully, you get a discount!`,
  "success"
);

Companies across industries are already putting this into practice — for example, a leading food manufacturer prevented promo abuse at scale, protecting both revenue and customer trust.

Explore our coupon abuse prevention demo

We built a fully open-source coupon abuse prevention demo to demonstrate the concepts above. Use this demo to see how you can use Fingerprint and simple logic rules to prevent promo abuse. If you have any questions or want to learn more about how Fingerprint can support your promo abuse prevention strategy, please contact our sales team.

Ready to stop promo abuse before it starts?

Install our JS agent on your website to uniquely identify the browsers that visit it.

All article tags

Share this post