Skip to main content

Overview

This tutorial walks through implementing Fingerprint to enforce regional pricing and prevent visitors from using VPNs or location spoofing to claim discounts intended for other regions. You’ll begin with a starter app that includes a mock checkout page and a discount pricing flow. From there, you’ll add the Fingerprint JavaScript agent to identify each visitor and use server-side logic with Fingerprint data to determine their real location and apply the correct regional price. You’ll also block VPN users from activating discounts they aren’t eligible for. By the end, you’ll have a sample app that enforces region-based pricing per visitor and can be customized to fit your use case and business rules. This tutorial uses just plain JavaScript and a Node server back end. For language- or framework-specific setups, see our quickstarts.
Estimated time: < 15 minutes
This tutorial requires the VPN Detection Smart Signal, which is only available on paid plans.

Prerequisites

Before you begin, make sure you have the following:
  • A copy of the starter repository (clone with Git or download as a ZIP)
  • Node.js (v20 or later) and npm installed
  • Your favorite code editor
  • Basic knowledge of JavaScript

1. Create a Fingerprint account and get your API keys

  1. Sign up for a free Fingerprint trial, or log in if you already have an account.
  2. After signing in, go to the API keys page in the dashboard.
  3. Save your public API key, which you’ll use to initialize the Fingerprint JavaScript agent.
  4. Create and securely store a secret API key for your server. Never expose it on the client side. You’ll use this key on the backend to retrieve full visitor information through the Fingerprint Server API.

2. Set up your project

  1. Clone or download the starter repository and open it in your editor.
Terminal
git clone https://github.com/fingerprintjs/use-case-tutorials.git
  1. This tutorial will be using the regional-pricing folder. The project is organized as follows:
Project structure
.
├── public/
│   ├── index.html        # Checkout page with regional pricing UI
│   └── index.js          # Front-end logic to activate regional pricing
├── server/
│   ├── discounts.js      # Regional pricing and location validation logic
│   └── server.js         # Serves static files and discount endpoint
└── .env.example          # Example environment variables
  1. Install dependencies:
Terminal
npm install
  1. Copy or rename .env.example to .env, then add your Fingerprint API keys:
Terminal
FP_PUBLIC_API_KEY=your-public-key
FP_SECRET_API_KEY=your-secret-key
  1. Start the server:
Terminal
npm run dev
  1. Visit http://localhost:3000 to view the mock checkout page from the starter app. You can test out the basic regional discount flow by clicking Activate local discount. You’ll notice that the discount is applied based on your location. Try manipulating the discount by turning on a VPN and selecting a server in another country then activating the local discount on this page again.

3. Add Fingerprint to the front end

In this step, you’ll load the Fingerprint client when the page loads and trigger identification when the user clicks Activate local discount. The client returns a requestId that you will send to your server. The server will then call the Fingerprint Events API to securely retrieve the full identification details, including location data and VPN detection.
  1. Before adding any new code, delete the client-side IP lookup. Specifically, open public/index.js and remove the getIP() function and any calls to it.
  2. At the top of public/index.js, load the Fingerprint JavaScript agent:
public/index.js
const fpPromise = import(
  `https://fpjscdn.net/v3/${window.FP_PUBLIC_API_KEY}`
).then((FingerprintJS) => FingerprintJS.load({ region: "us" }));
  1. Make sure to change region to match your workspace region (e.g., eu for Europe, ap for Asia, us for Global (default)).
  2. Near the bottom of public/index.js, the Activate local discount button already has an event handler for requesting a regional discount. Inside this handler, request visitor identification from Fingerprint using the get() method and include the returned requestId in the body instead of the client-side IP.
public/index.js
activateBtn.addEventListener("click", async () => {
  const fp = await fpPromise;
  const { requestId } = await fp.get();

  try {
    const res = await fetch("/api/region-discount", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ requestId }),
    });

    const data = await res.json();

	// ...
});
The get() method sends signals collected from the browser to Fingerprint servers, where they are analyzed to identify the visitor. The returned requestId acts as a reference to this specific identification event, which your server can later use to fetch the full visitor details. For lower latency in production, check out our documentation on using Sealed Client Results to return full identification details as an encrypted payload from the get() method.

4. Receive and use the request ID to get visitor insights

Next, pass the requestId through to your back end, initialize the Fingerprint Server API client, and fetch the full visitor identification event so you can access the full visitor details and Smart Signals.
  1. In the back end, the server/server.js file defines the API routes for the app. Update the /api/region-discount route to extract requestId from the request body and pass it into the getRegionDiscount function.
server/server.js
app.post("/api/region-discount", async (req, reply) => {
  const { requestId } = req.body;
  const result = await getRegionDiscount(requestId);
  return reply.send(result);
});
  1. The server/discounts.js file contains the logic for calculating regional discounts. Start by importing and initializing the Fingerprint Server API client there, and load your environment variables with dotenv.
server/discounts.js
import fs from "fs";
import { config } from "dotenv";
import {
  FingerprintJsServerApiClient,
  Region,
} from "@fingerprintjs/fingerprintjs-pro-server-api";

config();

const fpServerApiClient = new FingerprintJsServerApiClient({
  apiKey: process.env.FP_SECRET_API_KEY,
  region: Region.Global,
});
  1. Make sure to change region to match your workspace region (e.g., EU for Europe, AP for Asia, Global for Global (default)).
  2. Update the getRegionDiscount function to accept requestId and use it to fetch the full identification event details from Fingerprint:
server/discounts.js
export async function getRegionDiscount(requestId) {
  const event = await fpServerApiClient.getEvent(requestId);

  // ...
}
  1. Now retrieve the visitor’s real country directly from Fingerprint. Inside getRegionDiscount, read the ipLocation object from the identification event and extract the country code and name fields. This replaces the previous IP-based lookup:
server/discounts.js
export async function getRegionDiscount(requestId) {
  const event = await fpServerApiClient.getEvent(requestId);
  const ipLocation = event.products.identification.data.ipLocation;
  const countryCode = ipLocation.country.code;
  const countryName = ipLocation.country.name;

  // ...
}
Using the requestId, the Fingerprint server client will retrieve the full data for the visitor identification request. The returned object will contain the visitor ID, IP address, device, and browser details, and Smart Signals like bot detection, browser tampering detection, VPN detection, and more. You can see a full example of the event structure and test it with your own device in our demo playground. For additional checks to ensure the validity of the data coming from your front end, view how to protect from client-side tampering and replay attacks in our documentation.

5. Enforce VPN restrictions

For regional pricing, the core protection is making sure visitors aren’t using a VPN to appear as if they are in a cheaper region. Fingerprint includes a VPN detection Smart Signal that indicates whether the visitor is routing traffic through a VPN service. If a VPN is detected, the request should not receive any regional discount.
  1. Continuing in getRegionDiscount logic to read the VPN signal from the identification event and block discounted pricing whenever VPN usage is present:
server/discounts.js
export async function getRegionDiscount(requestId) {
  const event = await fpServerApiClient.getEvent(requestId);

  // ...

  const vpnDetected = event.products.vpn.data.result;
  if (vpnDetected) {
    console.error("VPN detected.");
    return {
      success: false,
      message:
        "VPN detected. Please turn off your VPN and use your normal connection before activating regional pricing.",
    };
  }
  // ...
}
This gives you a system to detect and block abusive regional pricing attempts. You can extend it by customizing how discounts are applied, adding country-specific rules, or layering in more Smart Signals based on your needs.
This is a minimal example to show how to implement Fingerprint. In a real application, make sure to implement proper security practices, error handling, and data handling that align with your production standards.

6. Block bots and suspicious devices

An additional way to protect your checkout is to block automated traffic from bots. The event object includes the Bot Detection Smart Signal that flags automated activity so you can deny discounted pricing to non-human traffic. This signal returns good for known bots like search engines, bad for automation tools, headless browsers, or other signs of automation, and notDetected when no bot activity is found.
  1. Continuing in the getRegionDiscount function in server/discounts.js, check the bot signal returned in the event object:
server/discounts.js
export async function getRegionDiscount(requestId) {
  // ...

  const botDetected = event.products?.botd?.data?.bot?.result !== "notDetected";

  if (botDetected) {
    console.error("Bot detected.");
    return {
      success: false,
      message: "No discount available for your location.",
    };
  }

  // ...
}
You can also use Fingerprint’s Suspect Score to flag suspicious regional pricing requests. The Suspect Score is a weighted representation of all Smart Signals in the identification payload and can help you detect high-risk traffic. While you wouldn’t normally deny regional pricing based only on this score, you could use it to flag the request for review or introduce additional checks.
  1. Below the bot detection check, add a condition that reads the Suspect Score from the event object and blocks the regional discount if it exceeds a chosen threshold (for example, 20):
server/discounts.js
export async function getRegionDiscount(requestId) {
  // ...

  const suspectScore = event.products?.suspectScore?.data?.result || 0;

  if (suspectScore > 20) {
    console.error(`High Suspect Score detected: ${suspectScore}`);
    return {
      success: false,
      message: "No discount available for your location.",
    };
  }

  // ...
}

7. Test your implementation

Now that everything is wired up, you can test the protected regional pricing flow using the checkout page.
  1. Start your server if it isn’t already running and open http://localhost:3000:
Terminal
npm run dev
  1. Click Activate local discount to fetch your country and apply the appropriate pricing.
  2. Try activating the discount again while connected to a VPN. The request will be blocked, and no regional pricing will be applied.
  3. Bonus: Test the flow using a headless browser or automation tool to see bot detection in action. A sample script is available in test-bot.js. While your app is running, run the script with node test-bot.js in your terminal and observe that automated discount attempts are also blocked.

Next steps

You now have a working regional pricing flow secured with Fingerprint. From here, you can expand the logic with more Smart Signals, adjust discount rules per market, or add extra checks for suspicious visitors. To dive deeper, explore our other use case tutorials for more step-by-step examples. Check out these related resources: