Tank · Code Alliance Developers
Termux Arsenalby Tank · Code Alliance

Leitura técnica · use o conhecimento de forma ética e autorizada

← estudos
Pagamentos18 min · avançado · atualizado 2026-06-29

Perfil pagador: autoavaliação e reparo via Termux (nível 2)

Como o ecossistema antifraude lê o seu “perfil pagador” (device, rede, comportamento) e como rodar uma autoavaliação em Python pelo Termux para identificar sinais ruins e reparar o que estiver causando challenge/recusa indevida.

🎧 audiobook · nível 1

Perfil pagador: autoavaliação e reparo via Termux (nível 2)

1/4
nível 1 · introdução acessívelnível 2 · em brevenível 3 · em breve

Continuação do nível 1 (3-D Secure 2.x)

Nível 2 da trilha de Pagamentos. Assumimos que você já entendeu os 5 atores, o fluxo frictionless × challenge e o Liability Shift. Aqui o foco é o lado do pagador real: por que transações legítimas viram challenge ou recusa, e como diagnosticar/reparar o próprio perfil sem cair em práticas que pioram ainda mais o risco.

O que é o “perfil pagador”

Antifraude moderno (Stripe Radar, Adyen RevenueProtect, Cybersource Decision Manager, Kount, Sift) não pontua só o cartão. Pontua o tripé: identidade (CPF, e-mail, telefone, histórico), dispositivo (device fingerprint, OS, fuso, idioma, integridade) e rede (IP, ASN, tipo de conexão, proximidade geográfica). O 3DS 2.x envia ~150 campos para a ACS do banco; o risco final é um score combinado dessas três dimensões.

  • Identidade ruim: e-mail recém-criado, telefone VOIP, CPF sem histórico no merchant.
  • Dispositivo ruim: fingerprint instável, fuso/idioma divergentes do BIN, root/jailbreak visível, automação detectada.
  • Rede ruim: IP residencial dinâmico mudando a cada compra, VPN comercial, ASN de hosting, geo longe do endereço de cobrança.
  • Comportamento ruim: várias tentativas com cartões diferentes, copiar/colar em campos sensíveis, velocidade anormal de preenchimento.

Escopo ético

Este artigo é sobre auditar o SEU próprio perfil (seu device, sua rede, suas compras legítimas) para reduzir falsos positivos. Não é guia de evasão de fraude: tentar maquiar sinais para passar como outra pessoa é crime (Lei 14.155/21, Art. 171-A CP). O caminho legítimo é reparar fundamentos.

Sinais que dá para medir pelo Termux

  • IP público e ASN (residencial vs. hosting vs. VPN comercial).
  • Geolocalização aproximada do IP vs. endereço de cobrança.
  • Fuso horário, locale e idioma do device.
  • Integridade do device (root, Magisk, su no PATH).
  • Higiene do e-mail (MX válido, domínio descartável).

Script Python — autoavaliação no Termux

O script abaixo coleta sinais públicos do seu próprio device/rede e gera um relatório com nota 0–100 por dimensão + recomendações de reparo. Roda 100% local exceto pela consulta a ipwho.is e DNS público para MX.

Pré-requisitos no Termux

pkg update -y && pkg upgrade -y
pkg install -y python git dnsutils
pip install --upgrade pip
pip install requests rich dnspython

~/payer_profile.py — autoavaliação do perfil pagador

#!/usr/bin/env python3
"""
payer_profile.py — autoavaliacao defensiva do perfil pagador.
Uso etico: rode SOMENTE no seu proprio device/rede para reduzir
falsos positivos em compras LEGITIMAS.
"""
from __future__ import annotations
import os, sys, json, time, locale, platform, shutil
from datetime import datetime, timezone
import requests
from rich.console import Console
from rich.table import Table
import dns.resolver

console = Console()
TIMEOUT = 8

# -------------------- coleta -------------------- #

def get_ip_info() -> dict:
    try:
        r = requests.get("https://ipwho.is/", timeout=TIMEOUT).json()
        c = r.get("connection") or {}
        return {
            "ip": r.get("ip"),
            "country": r.get("country_code"),
            "region": r.get("region"),
            "city": r.get("city"),
            "asn": c.get("asn"),
            "isp": c.get("isp"),
            "org": c.get("org"),
        }
    except Exception as e:
        return {"error": str(e)}

HOSTING = ("amazon","aws","google","gcp","azure","microsoft","digitalocean",
           "ovh","hetzner","linode","vultr","oracle","cloudflare","contabo",
           "m247","leaseweb","hostinger")
VPN = ("nordvpn","expressvpn","surfshark","protonvpn","mullvad",
       "private internet access","pia","cyberghost","ipvanish","windscribe")

def classify_network(info: dict):
    org = " ".join(str(v).lower() for v in (info.get("isp"), info.get("org")) if v)
    if any(k in org for k in VPN):     return ("VPN comercial detectada", 35)
    if any(k in org for k in HOSTING): return ("ASN de hosting/datacenter", 45)
    return ("Rede residencial/movel", 0)

def get_device_info() -> dict:
    try:
        lang = locale.getlocale()[0] or os.environ.get("LANG")
    except Exception:
        lang = os.environ.get("LANG")
    return {
        "os": f"{platform.system()} {platform.release()}",
        "arch": platform.machine(),
        "python": platform.python_version(),
        "timezone": time.strftime("%Z%z"),
        "locale": lang,
        "termux": bool(os.environ.get("TERMUX_VERSION")),
    }

def check_root():
    for c in ("su", "magisk"):
        if shutil.which(c): return True, c
    for p in ("/system/xbin/su","/system/bin/su","/sbin/su"):
        if os.path.exists(p): return True, p
    return False, ""

def check_email(email: str) -> dict:
    try:
        domain = email.split("@",1)[1].lower()
    except IndexError:
        return {"error": "e-mail invalido"}
    out = {"domain": domain}
    try:
        mx = sorted((r.preference, str(r.exchange).rstrip("."))
                    for r in dns.resolver.resolve(domain, "MX", lifetime=TIMEOUT))
        out["mx"] = [m[1] for m in mx]
    except Exception as e:
        out["mx_error"] = str(e)
    disposable = {"mailinator.com","tempmail.com","10minutemail.com",
                  "guerrillamail.com","yopmail.com","trashmail.com","getnada.com"}
    out["disposable"] = domain in disposable
    return out

# -------------------- scoring -------------------- #

def score(ip, dev, root, em, billing="BR"):
    network = device = identity = 100
    notes = []

    label, pen = classify_network(ip)
    network -= pen
    notes.append(f"Rede: {label} (-{pen}).")
    if billing and ip.get("country") and billing.upper() != str(ip["country"]).upper():
        network -= 25
        notes.append(f"Geo-IP ({ip['country']}) != pais de cobranca ({billing}) (-25).")

    if root[0]:
        device -= 40
        notes.append(f"Root/Magisk detectado: {root[1]} (-40).")
    if not (dev.get("locale") or "").lower().startswith(("pt","en")):
        device -= 10
        notes.append("Locale incomum para BIN BR (-10).")

    if em.get("disposable"):
        identity -= 60; notes.append("E-mail descartavel (-60).")
    if "mx_error" in em:
        identity -= 30; notes.append("Dominio sem MX valido (-30).")

    clamp = lambda x: max(0, min(100, x))
    return {"network": clamp(network), "device": clamp(device),
            "identity": clamp(identity), "notes": notes}

def recommendations(s):
    r = []
    if s["network"] < 70:
        r.append("Desligue VPN/proxy ao pagar; use 4G/5G da operadora ou WiFi residencial.")
        r.append("Evite redes corporativas com NAT pesado em primeira compra.")
    if s["device"] < 70:
        r.append("Pague pelo Chrome/Edge oficial, nao por WebView de app obscuro.")
        r.append("Device com root: use outro aparelho para compras de alto valor.")
        r.append("Mantenha fuso/idioma consistentes com o endereco de cobranca.")
    if s["identity"] < 70:
        r.append("Use e-mail com 6+ meses no merchant; evite descartaveis.")
        r.append("Cadastre telefone real (nao VOIP) — Adyen/Stripe usam carrier lookup.")
        r.append("Em first purchase, prefira cartoes ja usados antes no mesmo merchant.")
    if not r:
        r.append("Perfil saudavel. Se ainda houver atrito, e regra do EMISSOR — ligue no 0800 do banco e peca liberacao pre-compra.")
    return r

# -------------------- main -------------------- #

def main():
    email = sys.argv[1] if len(sys.argv) > 1 else input("Seu e-mail: ").strip()
    country = sys.argv[2] if len(sys.argv) > 2 else "BR"

    console.rule("[bold cyan]Coletando sinais")
    ip = get_ip_info(); dev = get_device_info()
    root = check_root(); em = check_email(email)

    t = Table(title="Snapshot do device/rede", show_lines=True)
    t.add_column("Campo"); t.add_column("Valor")
    for k, v in {**ip, **dev, "root": root[0], **em}.items():
        t.add_row(str(k), str(v))
    console.print(t)

    s = score(ip, dev, root, em, country)
    console.rule("[bold cyan]Scores (0-100, maior = menos risco)")
    console.print(f"Rede:        [bold]{s['network']}[/]")
    console.print(f"Dispositivo: [bold]{s['device']}[/]")
    console.print(f"Identidade:  [bold]{s['identity']}[/]")
    for n in s["notes"]:
        console.print(f"  - {n}")

    console.rule("[bold green]Recomendacoes de reparo")
    for rec in recommendations(s):
        console.print(f"-> {rec}")

    out = {"ts": datetime.now(timezone.utc).isoformat(),
           "ip": ip, "device": dev, "root": root[0],
           "email": em, "score": s}
    path = os.path.expanduser("~/payer_profile_report.json")
    with open(path, "w") as fp:
        json.dump(out, fp, indent=2, ensure_ascii=False)
    console.print(f"\n[dim]Relatorio salvo em {path}[/dim]")

if __name__ == "__main__":
    main()

Executar

python ~/payer_profile.py seu-email@dominio.com BR

Como interpretar o relatório

  1. Score Rede < 70 → o IP é o vilão. Trocar para 4G/5G da operadora antes da compra costuma reverter challenge.
  2. Score Dispositivo < 70 → fuso/idioma/integridade. Em devices com root, mover compras críticas para outro aparelho ajuda mais do que tentar esconder o Magisk.
  3. Score Identidade < 70 → e-mail/telefone. E-mail com 12+ meses no Gmail/Outlook + número celular real (não VOIP) destrava muita coisa.
  4. Três notas ≥ 80 e ainda toma recusa → o problema é regra do emissor (limite, MCC bloqueado, viagem). Resolve no 0800 do banco.

Não faça

Spoofar fingerprint, comprar proxy residencial, gerar identidades sintéticas, testar cartões de terceiros. Tudo isso é fraude tipificada (Lei 14.155/21) e os mesmos sistemas que pontuam o seu perfil detectam reuso de stack de spoofing em segundos — você piora o score em vez de melhorar.

Reparo estrutural (longo prazo)

  • Construir histórico no merchant: pequenas compras recorrentes com o mesmo cartão/e-mail/device elevam o trust score em semanas.
  • Manter um “device de pagamento”: Android atualizado, sem root, com apps oficiais — separa risco de experimentação.
  • E-mail principal estável (Gmail/Outlook/iCloud) com 2FA — vira identidade across merchants via Emailage/Ekata.
  • Atualizar endereço de cobrança no banco ao mudar de cidade; AVS divergente é uma das maiores causas de recusa silenciosa.

Ferramentas relacionadas

  • Nmapauditar a própria rede antes de classificá-la como residencial limpa
  • Termuxambiente onde o script roda; mantenha pacotes atualizados

Próximo nível (em breve)

Nível 3 vai cobrir: leitura de logs reais de declines (Stripe, Adyen, Cielo), mapeamento de códigos R/05/51/61 para causa-raiz, e como conversar com o emissor para liberar BIN e MCC específicos sem precisar trocar de banco.

Template OffSec · aplicar antes de rodar

Objetivo → Preparação → Execução → Evidência → Ética

→ guia do lab

Objetivo

Compreender protocolos (3DS, tokenização, PIX) e falhas comuns no checkout para reforçar antifraude — sem tocar em cartão real de terceiros.

Pré-requisitos

  • Ambiente sandbox do PSP com credenciais de teste
  • Coleção Postman/Insomnia com fluxos parametrizados
  • Cartões de teste oficiais (Visa 4111..., Mastercard 5555..., etc.)
  • Log estruturado (JSON) para cada request/response

Passos

  1. Mapear o fluxo (auth → 3DS challenge → capture → refund)
  2. Testar cada estado de erro documentado pelo PSP
  3. Injetar variações no device fingerprint e observar o desafio 3DS
  4. Medir latência e códigos de resposta para cada persona-teste
  5. Consolidar findings em playbook com decision tree para o time de risco

Evidências a coletar

  • Coleção Postman versionada
  • Log request/response com sanitização de PAN (só BIN + últimos 4)
  • Screenshots do painel do PSP mostrando a transação

Regra prática: se você não guardou evidência reprodutível, o achado não existe.

Guia prático (lab autorizado)

alvo · Sandbox oficial do PSP (Stripe test, Adyen test, PagSeguro sandbox)

curl https://api.stripe.com/v1/payment_intents -u sk_test_...: -d amount=1000 -d currency=brl -d 'payment_method_types[]=card'

verificar · O painel do PSP em modo teste é a fonte da verdade — compare com o response do seu backend. Divergência = bug no seu lado.

Checklist interativo

Checklist ético (marcar antes do primeiro comando)

Autorização, escopo, lab e proteção de dados. Sem 100%, não roda fora do lab.

0/5

Prática segura · Pagamentos

→ guia do lab

Monte um ambiente isolado antes de qualquer teste — use Docker (DVWA/juice-shop), uma VM host-only ou um alvo autorizado (HTB, TryHackMe, PortSwigger Academy).

Nunca aponte para alvo em produção. Se estiver no celular, use proot-distro + Debian para não sujar o Termux principal.