Check free-trial eligibility from a Python backend
Updated
From a Python backend, create an intent with the fpchecker package, let the browser or app identify the device with it, then call check_eligibility with the attestation and the user’s email. Grant the trial only when the answer says eligible is true. Treat every error as a refusal, and release the claim if you refund the trial.
The flow
Your backend is the only place that decides. The client identifies the device and hands back an opaque attestation; it never learns a score and never holds your secret key.
- Your backend creates an intent (create_intent) and gives the token to the client.
- The client identifies the device with the web agent or a mobile SDK and posts the attestation.
- Your backend calls check_eligibility and acts on the answer.
Install
The package needs Python 3.9 or later. Keep the secret key (sk_) in the environment of your backend only.
pip install fingerprintcheckerCreate the intent
Add subject when the user is already known, taking it from your own session and never from the request body. The decision call must then pass the same subject, so an attestation made for one user cannot be spent for another.
import os
from fpchecker import FpChecker, FpCheckerApiError
fp = FpChecker(os.environ["FPCHECKER_SECRET_KEY"])
@app.post("/trial/intent")
def trial_intent():
try:
out = fp.create_intent({
"purpose": "claim:trial",
"subject": str(current_user.id), # from your session
"clientIp": client_ip(request), # from your own edge
})
except FpCheckerApiError:
return {"error": "try_again"}, 503
return {"intentToken": out["intentToken"]}Check eligibility and grant
The answer is data even when it is a refusal, so read the body, not only the status code. Grant only on an explicit true. A refusal arrives as a normal 2xx body (eligible false, with an action and a reason). The package raises only for a network failure, a 5xx or a 400 such as a missing attestation; refuse and let the user retry in that case.
@app.post("/trial/start")
def trial_start():
attestation = request.json.get("attestation")
if not isinstance(attestation, str):
return {"ok": False, "error": "bad_request"}, 400
try:
verdict = fp.check_eligibility({
"attestation": attestation,
"subject": str(current_user.id),
"email": current_user.email,
"claim": True, # record the claim when the answer is allow
"idempotent": True, # a retry of the same claim answers allow again
"clientIp": client_ip(request),
})
except FpCheckerApiError:
return {"ok": False, "error": "try_again"}, 503 # raised: network, 5xx or 400. Fail closed
if verdict.get("eligible") is True and verdict.get("action") == "allow":
start_trial(current_user, claim_id=verdict.get("claimId")) # your code
return {"ok": True}
# A normal 2xx refusal. 'pow' or 'turnstile': a human check is needed. 'deny': show a neutral message (verdict["reason"] says why).
return {"ok": False, "action": verdict.get("action")}, 403Release a refunded claim
If you refund or cancel a trial, give the claim back with release_eligibility and the claimId you stored, so a genuine customer is not blocked later. The answer says whether it was released.
res = fp.release_eligibility({"claimId": claim_id})
if res.get("released") is not True:
log.warning("claim not released: %s", res.get("reason"))Counted allowances
For a free allowance that is counted per device instead of a one-time trial, create the intent with purpose quota:<name> and call check_quota. The same fail-closed rules apply.
Frequently asked questions
Does the client ever see the decision inputs?
No. The client gets an opaque attestation. The device id and trust score come back only to your backend, on the eligibility call.
Why do I check for exactly True?
A missing or malformed field must never read as permission. Comparing with True means only an explicit yes grants the trial.
Can I use the standard library instead of the package?
Yes. Every call is plain HTTP with your secret key. The package adds typed errors, retries for read calls and the sealed-result and webhook helpers.
Try FingerPrint Checker free
Every new account gets 2,000 free checks. No card required to start.
Create a free account