Entreguei um gate biométrico contra fraude em um mês, usando face-api.js para suprir o SDK mobile que não existia.

Cargo
Senior Software Engineer
Período
2021 — 2022, retornando em 2024 — 2025
Time
7
Stack
Angular · Ionic · Cordova · NgRx · face-api.js

Resumo

Uma onda de fraude em reembolsos batia nos apps de consumidor do grupo via compartilhamento de credenciais. Produto precisava de uma checagem de identidade do cliente, mas o fornecedor biométrico ainda não tinha SDK para Ionic/Cordova. Seguindo a ideia do Tech Lead, fiz a ponte com face-api.js: liveness on-device usando a transição de neutro para sorriso, status centralizado em NgRx, e um modal de recuperação para usuários com pedidos bloqueados. Entreguei dentro da biblioteca de saúde compartilhada para os dois apps herdarem a feature no próximo release. Voltei três anos depois para ajudar a migrar para o SDK nativo do fornecedor com um backend orientado a eventos.

Contexto

O grupo operava dois apps de consumidor. Um era um super-app cobrindo todos os produtos de seguro do cliente. O outro era um app de saúde focado, voltado para a maior base de apólices. O fluxo de saúde (rede credenciada, pedidos de reembolso, status de reembolso) ficava numa biblioteca npm compartilhada, instalada nos dois apps. Quando produto trouxe a necessidade de validação de identidade, a mudança precisava cair nesse código compartilhado para que os dois apps herdassem a feature ao mesmo tempo.

O desafio

A pressão vinha da fraude em reembolsos. Clínicas e terceiros estavam conseguindo credenciais dos segurados e abrindo pedidos falsos no nome deles. O fornecedor escolhido para validar identidade tinha SDK maduro para iOS e Android nativos, mas não para Ionic/Cordova, que era o stack dos apps. Precisávamos de um liveness check em produção rápido, antes do SDK mobile do fornecedor ficar pronto.

O que fizemos

  1. 01

    Resolvemos o gap do fornecedor com face-api.js

    O Tech Lead sugeriu usar face-api.js, uma biblioteca ML de frontend capaz de detectar a transição de rosto neutro para sorriso. Isso nos deu duas coisas de uma vez: uma prova básica de liveness (uma foto estática não faria a transição) e uma camada de captura que não dependia do SDK do fornecedor. Carreguei o modelo mais leve da biblioteca para reduzir o impacto no bundle, embora ainda fosse um custo real.

  2. 02

    Construímos a tela de captura com cordova-camera-preview

    A tela de captura usava o cordova-camera-preview para renderizar o feed da câmera com um placeholder de centralização do rosto, então o usuário se enquadrava sem precisar trocar para o app de câmera nativo. Quando o face-api.js reportava um rosto detectado, eu capturava uma foto. Quando também reportava sorriso, capturava uma segunda. As duas imagens viravam o payload de liveness. O backend enviava ao fornecedor para comparação contra o documento do cliente.

  3. 03

    Centralizamos o status biométrico em NgRx

    Na abertura do app, eu consultava o backend por qualquer análise vinculada ao usuário, em andamento ou já encerrada. O resultado hidratava uma slice de NgRx. Se o cliente tinha um pedido bloqueado esperando por causa de uma checagem falha, abrir o app abria um modal com um único CTA para refazer a validação. Isso evitava que o gate de fraude virasse um beco sem saída silencioso.

  4. 04

    Entregamos dentro da biblioteca compartilhada de saúde

    Como captura, status e modal viviam na biblioteca npm compartilhada consumida pelos dois apps, ambos pegaram a mudança no próximo release. As instalações combinadas só no Google Play passavam de milhões, então o reuso pesava.

Resultados

~1 mês

MVP em produção

do kickoff até os dois apps publicarem o gate biométrico

2 apps

Reuso via biblioteca compartilhada

6M+ downloads combinados no Google Play à época

3 anos

Operou como primeira porta

até o time migrar para o SDK nativo do fornecedor, onde voltei para contribuir

Em retrospecto

  • O backend existia, mas suas respostas espelhavam o formato de payload do fornecedor de perto, e a slice de NgRx no mobile foi modelada sobre esse formato. Cada campo vazado da API do fornecedor terminava no estado do mobile. Hoje eu desenharia um contrato estável no backend que não vaza especificidades do fornecedor: um enum de status pequeno (needs-check, in-progress, valid, blocked) com uma string opcional de motivo. Aí a migração de 2024-2025 para o SDK nativo vira mudança só no backend, e o mobile não sabe qual fornecedor está rodando o gate.
  • A camada de captura ficava inteira em TypeScript sobre cordova-camera-preview e face-api.js. Isso comprou velocidade ao custo de peso de bundle, performance de runtime, e um teto pro que ML on-device conseguia fazer dentro de webview. Hoje eu exploraria um plugin Cordova ou Capacitor custom fazendo captura e detecção de rosto nativamente, expondo uma superfície JS limpa pro app. Eu não tinha base de Swift ou Kotlin à época, só Java leve. É um gap que fecharia ou montaria time hoje.