🔑 Fluxo de Recuperação de Senha
Este guia apresenta o fluxo completo para usuários que esqueceram a senha e precisam redefinir o acesso.
📋 Visão Geral
O fluxo de recuperação de senha permite que usuários redefinam suas credenciais de forma segura, passando por validações de identidade.
Este fluxo deve ser iniciado quando:
- Usuário clica em "Esqueci minha senha"
- Após múltiplas tentativas de login incorretas
- Por solicitação explícita do usuário
URLs Base:
- Homologação:
https://apihml.credsystem.com.br/api/v1 - Produção:
https://api.credsystem.com.br/api/v1
- 🔧 Guia de Troubleshooting - Solução de problemas e erros comuns
- 📖 Especificação OpenAPI — Referência técnica completa (schemas, tipos, exemplos)
1️⃣ Validar Dados do Cliente
Inicie o fluxo de recuperação validando CPF e data de nascimento do usuário.
Endpoint
POST {baseUrl}/identificacao/dados
Content-Type: application/json
Request Body
{
"app": "APP_NAME",
"fluxo": "RECUPERAR_SENHA",
"cliente": {
"cpf": "12345678909",
"dataNascimento": "1990-01-01"
},
"dispositivo": {
"dispositivoId": "device123",
"idSistemaOperacional": "2",
"versaoSistemaOperacional": "12",
"modelo": "Samsung Galaxy",
"versaoApp": "1.0.0"
}
}
Parâmetros
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
app | string | ✅ | Nome do aplicativo configurado |
fluxo | string | ✅ | Fixo: "RECUPERAR_SENHA" |
cliente.cpf | string | ✅ | CPF do cliente (apenas números) |
cliente.dataNascimento | string | ✅ | Data de nascimento (YYYY-MM-DD) |
dispositivo.dispositivoId | string | ✅ | ID único do dispositivo |
dispositivo.idSistemaOperacional | string | ✅ | 1=iOS, 2=Android, 3=Outros |
dispositivo.versaoSistemaOperacional | string | ✅ | Versão do SO |
dispositivo.modelo | string | ✅ | Modelo do dispositivo |
dispositivo.versaoApp | string | ✅ | Versão do aplicativo |
Note que o campo fluxo agora é "RECUPERAR_SENHA" em vez de "PRIMEIRO_ACESSO".
Resposta de Sucesso
{
"access_token": "eyJhbGci...",
"token_type": "Bearer",
"expires_in": 900
}
Este token expira em 15 minutos. Complete o fluxo dentro deste prazo.
Exemplos de Código
- cURL
- JavaScript
- Python
- Java
- C#
- PHP
- Go
curl -X POST "{baseUrl}/identificacao/dados" \
-H "Content-Type: application/json" \
-d '{
"app": "meu-app-private-label",
"fluxo": "RECUPERAR_SENHA",
"cliente": {
"cpf": "12345678909",
"dataNascimento": "1990-01-01"
},
"dispositivo": {
"dispositivoId": "device123",
"idSistemaOperacional": "2",
"versaoSistemaOperacional": "12",
"modelo": "Samsung Galaxy",
"versaoApp": "1.0.0"
}
}'
async function iniciarRecuperacaoSenha(cpf, dataNascimento, dispositivo) {
const response = await fetch(`${baseUrl}/identificacao/dados`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
app: 'meu-app-private-label',
fluxo: 'RECUPERAR_SENHA',
cliente: { cpf, dataNascimento },
dispositivo
})
});
if (!response.ok) throw new Error('Erro ao iniciar recuperação de senha');
const { access_token } = await response.json();
sessionStorage.setItem('recovery_token', access_token);
return access_token;
}
import requests
def iniciar_recuperacao_senha(base_url, cpf, data_nascimento, dispositivo):
url = f"{base_url}/identificacao/dados"
payload = {
"app": "meu-app-private-label",
"fluxo": "RECUPERAR_SENHA",
"cliente": {"cpf": cpf, "dataNascimento": data_nascimento},
"dispositivo": dispositivo
}
response = requests.post(url, json=payload)
response.raise_for_status()
return response.json()["access_token"]
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.http.*;
import java.net.URI;
import java.util.Map;
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> body = Map.of(
"app", "meu-app-private-label",
"fluxo", "RECUPERAR_SENHA",
"cliente", Map.of("cpf", cpf, "dataNascimento", dataNascimento),
"dispositivo", dispositivo
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/identificacao/dados"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(body)))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
String accessToken = mapper.readTree(response.body()).get("access_token").asText();
using System.Net.Http.Json;
using System.Text.Json;
var client = new HttpClient();
var payload = new {
app = "meu-app-private-label",
fluxo = "RECUPERAR_SENHA",
cliente = new { cpf, dataNascimento },
dispositivo
};
var response = await client.PostAsJsonAsync($"{baseUrl}/identificacao/dados", payload);
response.EnsureSuccessStatusCode();
var data = await response.Content.ReadFromJsonAsync<JsonElement>();
var accessToken = data.GetProperty("access_token").GetString();
$payload = [
'app' => 'meu-app-private-label',
'fluxo' => 'RECUPERAR_SENHA',
'cliente' => ['cpf' => $cpf, 'dataNascimento' => $dataNascimento],
'dispositivo' => $dispositivo,
];
$ch = curl_init("{$baseUrl}/identificacao/dados");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
$accessToken = $data['access_token'];
import (
"bytes"
"encoding/json"
"net/http"
)
payload := map[string]interface{}{
"app": "meu-app-private-label",
"fluxo": "RECUPERAR_SENHA",
"cliente": map[string]string{"cpf": cpf, "dataNascimento": dataNascimento},
"dispositivo": dispositivo,
}
body, _ := json.Marshal(payload)
resp, _ := http.Post(baseURL+"/identificacao/dados", "application/json", bytes.NewBuffer(body))
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
accessToken := result["access_token"].(string)
Consulte o 🔧 Troubleshooting - Validar Dados para detalhes de erros e soluções.
2️⃣ Solicitar Link de Biometria
Solicite o link de captura biométrica que será aberto no WebView do aplicativo para validação facial do usuário.
O link retornado por este endpoint deve ser integrado via WebView usando o Motor de Biometria Credsystem. Consulte a documentação por plataforma:
Acesse a Visão Geral do Motor de Biometria para entender o fluxo completo de captura.
Endpoint
POST {baseUrl}/identificacao/biometria
Authorization: Bearer {token-sessao-cliente}
Content-Type: application/json
Request Body
{
"consumidor": "CONSUMIDOR_ID",
"loja": 123456
}
Exemplos de Código
- cURL
- JavaScript
- Python
- Java
- C#
- PHP
- Go
curl -X POST "{baseUrl}/identificacao/biometria" \
-H "Authorization: Bearer {token-sessao-cliente}" \
-H "Content-Type: application/json" \
-d '{"consumidor": "CONSUMIDOR_ID", "loja": 123456}'
async function solicitarBiometriaRecuperacao(sessionToken, consumidorId, lojaId) {
const response = await fetch(`${baseUrl}/identificacao/biometria`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${sessionToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ consumidor: consumidorId, loja: lojaId })
});
if (!response.ok) throw new Error('Erro ao solicitar biometria');
const { link } = await response.json();
return link;
}
import requests
def solicitar_biometria_recuperacao(base_url, session_token, consumidor_id, loja_id):
url = f"{base_url}/identificacao/biometria"
headers = {"Authorization": f"Bearer {session_token}", "Content-Type": "application/json"}
response = requests.post(url, json={"consumidor": consumidor_id, "loja": loja_id}, headers=headers)
response.raise_for_status()
return response.json()["link"]
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.http.*;
import java.net.URI;
import java.util.Map;
ObjectMapper mapper = new ObjectMapper();
String body = mapper.writeValueAsString(Map.of("consumidor", consumidorId, "loja", lojaId));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/identificacao/biometria"))
.header("Authorization", "Bearer " + sessionToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
String link = mapper.readTree(response.body()).get("link").asText();
using System.Net.Http.Json;
using System.Text.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", sessionToken);
var response = await client.PostAsJsonAsync(
$"{baseUrl}/identificacao/biometria",
new { consumidor = consumidorId, loja = lojaId }
);
response.EnsureSuccessStatusCode();
var data = await response.Content.ReadFromJsonAsync<JsonElement>();
var link = data.GetProperty("link").GetString();
$ch = curl_init("{$baseUrl}/identificacao/biometria");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$sessionToken}",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'consumidor' => $consumidorId, 'loja' => $lojaId
]));
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
$link = $data['link'];
import (
"bytes"
"encoding/json"
"net/http"
)
payload := map[string]interface{}{"consumidor": consumidorId, "loja": lojaId}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", baseURL+"/identificacao/biometria", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+sessionToken)
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
link := result["link"].(string)
Abra o link retornado em um WebView e aguarde o callback de conclusão da captura biométrica antes de avançar para o próximo passo do fluxo.
Consulte o 🔧 Troubleshooting - Solicitar Biometria para detalhes de erros e soluções.
3️⃣ Enviar Token SMS
Solicite o envio de um código de verificação por SMS para o telefone cadastrado.
Endpoint
POST {baseUrl}/identificacao/token-sms/envio
Authorization: Bearer {token-sessao-cliente}
Exemplos de Código
- cURL
- JavaScript
- Python
- Java
- C#
- PHP
- Go
curl -X POST "{baseUrl}/identificacao/token-sms/envio" \
-H "Authorization: Bearer {token-sessao-cliente}"
async function enviarSMSRecuperacao(sessionToken) {
const response = await fetch(`${baseUrl}/identificacao/token-sms/envio`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${sessionToken}` }
});
if (!response.ok) throw new Error('Erro ao enviar SMS');
return await response.json();
}
import requests
def enviar_sms_recuperacao(base_url, session_token):
url = f"{base_url}/identificacao/token-sms/envio"
headers = {"Authorization": f"Bearer {session_token}"}
response = requests.post(url, headers=headers)
response.raise_for_status()
return response.json()
import java.net.http.*;
import java.net.URI;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/identificacao/token-sms/envio"))
.header("Authorization", "Bearer " + sessionToken)
.POST(HttpRequest.BodyPublishers.noBody())
.build();
HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", sessionToken);
var response = await client.PostAsync(
$"{baseUrl}/identificacao/token-sms/envio", null
);
response.EnsureSuccessStatusCode();
$ch = curl_init("{$baseUrl}/identificacao/token-sms/envio");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer {$sessionToken}"]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '');
$response = curl_exec($ch);
curl_close($ch);
import "net/http"
req, _ := http.NewRequest("POST", baseURL+"/identificacao/token-sms/envio", nil)
req.Header.Set("Authorization", "Bearer "+sessionToken)
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
- Código de 6 dígitos
- Válido por 5 minutos
- Enviado para o telefone cadastrado no sistema
- Uso único
Consulte o 🔧 Troubleshooting - Enviar SMS para detalhes de erros e soluções.
4️⃣ Validar Token SMS
Valide o código de 6 dígitos recebido por SMS.
Endpoint
POST {baseUrl}/identificacao/token-sms
Authorization: Bearer {token-sessao-cliente}
Content-Type: application/json
Request Body
{
"pinCode": "123456",
"analise": {
"userID": "12345678909",
"origem": "APP_NAME",
"deviceID": "device123",
"integrationName": "app-login-pl",
"dadosMaquina": {
"ip": "192.168.0.1",
"fingerPrint": "{fingerprint}"
}
}
}
Exemplos de Código
- cURL
- JavaScript
- Python
- Java
- C#
- PHP
- Go
curl -X POST "{baseUrl}/identificacao/token-sms" \
-H "Authorization: Bearer {token-sessao-cliente}" \
-H "Content-Type: application/json" \
-d '{
"pinCode": "123456",
"analise": {
"userID": "12345678909",
"origem": "APP_NAME",
"deviceID": "device123",
"integrationName": "app-login-pl",
"dadosMaquina": {"ip": "192.168.0.1", "fingerPrint": "{fingerprint}"}
}
}'
async function validarSMSRecuperacao(sessionToken, pinCode, dadosDispositivo) {
const response = await fetch(`${baseUrl}/identificacao/token-sms`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${sessionToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
pinCode,
analise: {
userID: dadosDispositivo.cpf,
origem: 'meu-app-private-label',
deviceID: dadosDispositivo.dispositivoId,
integrationName: 'app-login-pl',
dadosMaquina: { ip: dadosDispositivo.ip, fingerPrint: dadosDispositivo.fingerprint }
}
})
});
if (!response.ok) throw new Error('Código SMS inválido');
return await response.json();
}
import requests
def validar_sms_recuperacao(base_url, session_token, pin_code, dados_dispositivo):
url = f"{base_url}/identificacao/token-sms"
headers = {
"Authorization": f"Bearer {session_token}",
"Content-Type": "application/json"
}
payload = {
"pinCode": pin_code,
"analise": {
"userID": dados_dispositivo["cpf"],
"origem": "meu-app-private-label",
"deviceID": dados_dispositivo["dispositivoId"],
"integrationName": "app-login-pl",
"dadosMaquina": {
"ip": dados_dispositivo["ip"],
"fingerPrint": dados_dispositivo["fingerprint"]
}
}
}
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
return response.json()
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.http.*;
import java.net.URI;
import java.util.Map;
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> body = Map.of(
"pinCode", pinCode,
"analise", Map.of(
"userID", cpf, "origem", "meu-app-private-label",
"deviceID", dispositivoId, "integrationName", "app-login-pl",
"dadosMaquina", Map.of("ip", ip, "fingerPrint", fingerprint)
)
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/identificacao/token-sms"))
.header("Authorization", "Bearer " + sessionToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(body)))
.build();
HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
using System.Net.Http.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", sessionToken);
var payload = new {
pinCode,
analise = new {
userID = cpf,
origem = "meu-app-private-label",
deviceID = dispositivoId,
integrationName = "app-login-pl",
dadosMaquina = new { ip, fingerPrint = fingerprint }
}
};
var response = await client.PostAsJsonAsync($"{baseUrl}/identificacao/token-sms", payload);
response.EnsureSuccessStatusCode();
$payload = [
'pinCode' => $pinCode,
'analise' => [
'userID' => $cpf,
'origem' => 'meu-app-private-label',
'deviceID' => $dispositivoId,
'integrationName' => 'app-login-pl',
'dadosMaquina' => ['ip' => $ip, 'fingerPrint' => $fingerprint]
]
];
$ch = curl_init("{$baseUrl}/identificacao/token-sms");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$sessionToken}",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
$response = curl_exec($ch);
curl_close($ch);
import (
"bytes"
"encoding/json"
"net/http"
)
payload := map[string]interface{}{
"pinCode": pinCode,
"analise": map[string]interface{}{
"userID": cpf, "origem": "meu-app-private-label",
"deviceID": dispositivoId, "integrationName": "app-login-pl",
"dadosMaquina": map[string]string{"ip": ip, "fingerPrint": fingerprint},
},
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", baseURL+"/identificacao/token-sms", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+sessionToken)
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
O objeto analise é usado para detecção de fraudes. Envie informações precisas do dispositivo.
Consulte o 🔧 Troubleshooting - Validar SMS para detalhes de erros e soluções.
5️⃣ Buscar Termo de Aceite
Obtenha o texto do termo de uso atualizado que o usuário deve aceitar.
Endpoint
GET {baseUrl}/contratos/termo-aceite
Authorization: Bearer {token-sessao-cliente}
Exemplos de Código
- cURL
- JavaScript
- Python
- Java
- C#
- PHP
- Go
curl -X GET "{baseUrl}/contratos/termo-aceite" \
-H "Authorization: Bearer {token-sessao-cliente}"
async function buscarTermoRecuperacao(sessionToken) {
const response = await fetch(`${baseUrl}/contratos/termo-aceite`, {
headers: { 'Authorization': `Bearer ${sessionToken}` }
});
if (!response.ok) throw new Error('Erro ao buscar termo de aceite');
const { termo } = await response.json();
return termo;
}
import requests
def buscar_termo_recuperacao(base_url, session_token):
url = f"{base_url}/contratos/termo-aceite"
headers = {"Authorization": f"Bearer {session_token}"}
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()["termo"]
import java.net.http.*;
import java.net.URI;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/contratos/termo-aceite"))
.header("Authorization", "Bearer " + sessionToken)
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
using System.Net.Http;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", sessionToken);
var response = await client.GetAsync($"{baseUrl}/contratos/termo-aceite");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
$ch = curl_init("{$baseUrl}/contratos/termo-aceite");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer {$sessionToken}"]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
$termo = $data['termo'];
import (
"encoding/json"
"net/http"
)
req, _ := http.NewRequest("GET", baseURL+"/contratos/termo-aceite", nil)
req.Header.Set("Authorization", "Bearer "+sessionToken)
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
termo := result["termo"]
Exemplo de Resposta
{
"termo": {
"id": 123,
"versao": "2.1",
"titulo": "Termos de Uso e Política de Privacidade",
"conteudo": "Texto completo do termo...",
"dataPublicacao": "2025-01-01T00:00:00Z"
}
}
Consulte o 🔧 Troubleshooting - Termo de Aceite para detalhes de erros e soluções.
6️⃣ Aceitar Termo
Registre a aceitação do termo pelo usuário.
Endpoint
PUT {baseUrl}/contratos/termo-aceite
Authorization: Bearer {token-sessao-cliente}
Content-Type: application/json
Request Body
{
"aceite": true
}
Exemplos de Código
- cURL
- JavaScript
- Python
- Java
- C#
- PHP
- Go
curl -X PUT "{baseUrl}/contratos/termo-aceite" \
-H "Authorization: Bearer {token-sessao-cliente}" \
-H "Content-Type: application/json" \
-d '{"aceite": true}'
async function aceitarTermoRecuperacao(sessionToken) {
const response = await fetch(`${baseUrl}/contratos/termo-aceite`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${sessionToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ aceite: true })
});
if (!response.ok) throw new Error('Erro ao aceitar termo');
return await response.json();
}
import requests
def aceitar_termo_recuperacao(base_url, session_token):
url = f"{base_url}/contratos/termo-aceite"
headers = {
"Authorization": f"Bearer {session_token}",
"Content-Type": "application/json"
}
response = requests.put(url, json={"aceite": True}, headers=headers)
response.raise_for_status()
return response.json()
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.http.*;
import java.net.URI;
import java.util.Map;
String body = new ObjectMapper().writeValueAsString(Map.of("aceite", true));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/contratos/termo-aceite"))
.header("Authorization", "Bearer " + sessionToken)
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
using System.Net.Http.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", sessionToken);
var response = await client.PutAsJsonAsync(
$"{baseUrl}/contratos/termo-aceite",
new { aceite = true }
);
response.EnsureSuccessStatusCode();
$ch = curl_init("{$baseUrl}/contratos/termo-aceite");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$sessionToken}",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['aceite' => true]));
$response = curl_exec($ch);
curl_close($ch);
import (
"bytes"
"encoding/json"
"net/http"
)
body, _ := json.Marshal(map[string]bool{"aceite": true})
req, _ := http.NewRequest("PUT", baseURL+"/contratos/termo-aceite", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+sessionToken)
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
Sempre apresente o termo completo ao usuário e obtenha consentimento explícito antes de enviar o aceite.
Consulte o 🔧 Troubleshooting - Termo de Aceite para detalhes de erros e soluções.
7️⃣ Definir Nova Senha
Finalize o processo criando uma nova senha para o usuário.
Endpoint
POST {baseUrl}/usuario/acesso
Authorization: Bearer {token-sessao-cliente}
Content-Type: application/json
Request Body
{
"senha": "NovaSenhaSegura123"
}
Exemplos de Código
- cURL
- JavaScript
- Python
- Java
- C#
- PHP
- Go
curl -X POST "{baseUrl}/usuario/acesso" \
-H "Authorization: Bearer {token-sessao-cliente}" \
-H "Content-Type: application/json" \
-d '{"senha": "NovaSenhaSegura123!"}'
function validarForcaSenha(senha) {
return senha.length >= 8
&& /[A-Z]/.test(senha) && /[a-z]/.test(senha)
&& /[0-9]/.test(senha) && /[!@#$%^&*(),.?":{}|<>]/.test(senha);
}
async function definirNovaSenha(sessionToken, novaSenha, confirmacaoSenha) {
if (novaSenha !== confirmacaoSenha) throw new Error('As senhas não coincidem');
if (!validarForcaSenha(novaSenha)) throw new Error('A senha não atende aos requisitos de segurança');
const response = await fetch(`${baseUrl}/usuario/acesso`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${sessionToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ senha: novaSenha })
});
if (!response.ok) throw new Error('Erro ao definir nova senha');
return await response.json();
}
import re, requests
def definir_nova_senha(base_url, session_token, nova_senha):
if len(nova_senha) < 8 or not re.search(r'[A-Z]', nova_senha):
raise ValueError('Senha não atende aos requisitos')
url = f"{base_url}/usuario/acesso"
headers = {
"Authorization": f"Bearer {session_token}",
"Content-Type": "application/json"
}
response = requests.post(url, json={"senha": nova_senha}, headers=headers)
response.raise_for_status()
return response.json()
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.http.*;
import java.net.URI;
import java.util.Map;
String body = new ObjectMapper().writeValueAsString(Map.of("senha", novaSenha));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/usuario/acesso"))
.header("Authorization", "Bearer " + sessionToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
using System.Net.Http.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", sessionToken);
var response = await client.PostAsJsonAsync(
$"{baseUrl}/usuario/acesso",
new { senha = novaSenha }
);
response.EnsureSuccessStatusCode();
$ch = curl_init("{$baseUrl}/usuario/acesso");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$sessionToken}",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['senha' => $novaSenha]));
$response = curl_exec($ch);
curl_close($ch);
import (
"bytes"
"encoding/json"
"net/http"
)
body, _ := json.Marshal(map[string]string{"senha": novaSenha})
req, _ := http.NewRequest("POST", baseURL+"/usuario/acesso", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+sessionToken)
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
O usuário já pode fazer login com a nova senha definida.
Requisitos obrigatórios para senha segura:
- Mínimo de 8 caracteres
- Pelo menos 1 letra maiúscula
- Pelo menos 1 letra minúscula
- Pelo menos 1 número
- Pelo menos 1 caractere especial (!@#$%^&*)
Consulte o 🔧 Troubleshooting - Criar/Atualizar Senha para detalhes de erros e soluções.
🔄 Fluxo Completo - Exemplo
async function fluxoRecuperacaoSenhaCompleto(dadosCliente, dadosDispositivo) {
try {
// Passo 1: Validar dados
console.log('🔍 Validando dados do cliente...');
const sessionToken = await iniciarRecuperacaoSenha(
dadosCliente.cpf,
dadosCliente.dataNascimento,
dadosDispositivo
);
exibirMensagem('Dados validados com sucesso');
// Passo 2: Biometria (opcional)
// const linkBiometria = await solicitarBiometriaRecuperacao(sessionToken, consumidorId, lojaId);
// Passo 3: Enviar SMS
console.log('📱 Enviando código SMS...');
await enviarSMSRecuperacao(sessionToken);
exibirMensagem('Código SMS enviado para seu telefone cadastrado');
// Aguardar usuário digitar código
const pinCode = await solicitarCodigoSMS();
// Passo 4: Validar SMS
console.log('✔️ Validando código SMS...');
await validarSMSRecuperacao(sessionToken, pinCode, dadosDispositivo);
exibirMensagem('Código validado com sucesso');
// Passo 5: Buscar termo
console.log('📄 Buscando termo de aceite...');
const termo = await buscarTermoRecuperacao(sessionToken);
// Exibir termo ao usuário
await exibirTermo(termo);
// Aguardar aceite
const aceitou = await aguardarAceiteUsuario();
if (!aceitou) {
throw new Error('Usuário recusou o termo de aceite');
}
// Passo 6: Aceitar termo
console.log('✅ Registrando aceite do termo...');
await aceitarTermoRecuperacao(sessionToken);
// Passo 7: Criar nova senha
console.log('🔑 Aguardando nova senha...');
const { novaSenha, confirmacaoSenha } = await solicitarNovaSenha();
await definirNovaSenha(sessionToken, novaSenha, confirmacaoSenha);
console.log('🎉 Senha recuperada com sucesso!');
exibirSucesso('Senha alterada com sucesso! Você já pode fazer login.');
// Limpar sessão temporária
sessionStorage.removeItem('recovery_token');
// Redirecionar para tela de login
setTimeout(() => {
navegarParaLogin();
}, 2000);
} catch (error) {
console.error('❌ Erro na recuperação de senha:', error);
if (error.message.includes('CPF não encontrado')) {
exibirErro('CPF não encontrado ou data de nascimento incorreta.');
} else if (error.message.includes('Código SMS inválido')) {
exibirErro('Código incorreto. Tente novamente.');
} else if (error.message.includes('Token expirado')) {
exibirErro('Tempo expirado. Por favor, inicie o processo novamente.');
navegarParaRecuperarSenha();
} else {
exibirErro('Erro ao recuperar senha. Tente novamente mais tarde.');
}
throw error;
}
}
🎨 Interface do Usuário - Exemplos
1. Tela Inicial de Recuperação
function TelaRecuperarSenha() {
const [cpf, setCpf] = useState('');
const [dataNascimento, setDataNascimento] = useState('');
const [carregando, setCarregando] = useState(false);
const handleRecuperar = async () => {
setCarregando(true);
try {
await iniciarRecuperacaoSenha(cpf, dataNascimento, obterDadosDispositivo());
navegarParaValidacaoSMS();
} catch (error) {
Alert.alert('Erro', error.message);
} finally {
setCarregando(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Recuperar Senha</Text>
<Text style={styles.subtitle}>
Digite seus dados para recuperar o acesso à sua conta
</Text>
<Input
label="CPF"
value={cpf}
onChangeText={formatarCPF}
keyboardType="numeric"
maxLength={14}
placeholder="000.000.000-00"
/>
<Input
label="Data de Nascimento"
value={dataNascimento}
onChangeText={formatarData}
keyboardType="numeric"
maxLength={10}
placeholder="DD/MM/AAAA"
/>
<Button
title="Continuar"
onPress={handleRecuperar}
loading={carregando}
disabled={!cpf || !dataNascimento}
/>
<Link onPress={() => navegarParaLogin()}>
Voltar para login
</Link>
</View>
);
}
2. Validação de SMS com Timer
function TelaValidacaoSMS() {
const [codigo, setCodigo] = useState('');
const [tempoRestante, setTempoRestante] = useState(300); // 5 minutos
const [podeReenviar, setPodeReenviar] = useState(false);
useEffect(() => {
const timer = setInterval(() => {
setTempoRestante(prev => {
if (prev <= 1) {
setPodeReenviar(true);
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(timer);
}, []);
const formatarTempo = (segundos) => {
const min = Math.floor(segundos / 60);
const seg = segundos % 60;
return `${min}:${seg.toString().padStart(2, '0')}`;
};
const handleReenviar = async () => {
try {
await enviarSMSRecuperacao(sessionToken);
setTempoRestante(300);
setPodeReenviar(false);
Alert.alert('Sucesso', 'Novo código enviado!');
} catch (error) {
Alert.alert('Erro', error.message);
}
};
return (
<View style={styles.container}>
<Icon name="message" size={64} color="#4CAF50" />
<Text style={styles.title}>Código SMS</Text>
<Text style={styles.subtitle}>
Digite o código de 6 dígitos enviado para seu telefone
</Text>
<CodeInput
value={codigo}
onChangeText={setCodigo}
cellCount={6}
autoFocus
/>
<View style={styles.timerContainer}>
{tempoRestante > 0 ? (
<Text>Código expira em {formatarTempo(tempoRestante)}</Text>
) : (
<Text style={styles.expired}>Código expirado</Text>
)}
</View>
{podeReenviar ? (
<Button
title="Reenviar código"
onPress={handleReenviar}
variant="outline"
/>
) : (
<Text style={styles.hint}>
Não recebeu? Aguarde para reenviar
</Text>
)}
<Button
title="Validar"
onPress={() => validarCodigo(codigo)}
disabled={codigo.length !== 6}
/>
</View>
);
}
3. Definição de Nova Senha com Validação Visual
function TelaNovaSenha() {
const [senha, setSenha] = useState('');
const [confirmacao, setConfirmacao] = useState('');
const [mostrarSenha, setMostrarSenha] = useState(false);
const validacoes = {
tamanho: senha.length >= 8,
maiuscula: /[A-Z]/.test(senha),
minuscula: /[a-z]/.test(senha),
numero: /[0-9]/.test(senha),
especial: /[!@#$%^&*(),.?":{}|<>]/.test(senha),
coincide: senha === confirmacao && senha.length > 0
};
const senhaValida = Object.values(validacoes).every(v => v);
return (
<View style={styles.container}>
<Text style={styles.title}>Criar Nova Senha</Text>
<Text style={styles.subtitle}>
Escolha uma senha forte para proteger sua conta
</Text>
<Input
label="Nova senha"
value={senha}
onChangeText={setSenha}
secureTextEntry={!mostrarSenha}
rightIcon={
<IconButton
icon={mostrarSenha ? 'eye-off' : 'eye'}
onPress={() => setMostrarSenha(!mostrarSenha)}
/>
}
/>
<Input
label="Confirmar senha"
value={confirmacao}
onChangeText={setConfirmacao}
secureTextEntry={!mostrarSenha}
/>
<View style={styles.requisitos}>
<Text style={styles.requisitosTitle}>Requisitos da senha:</Text>
<RequisitoItem
atendido={validacoes.tamanho}
texto="Mínimo 8 caracteres"
/>
<RequisitoItem
atendido={validacoes.maiuscula}
texto="Pelo menos 1 letra maiúscula"
/>
<RequisitoItem
atendido={validacoes.minuscula}
texto="Pelo menos 1 letra minúscula"
/>
<RequisitoItem
atendido={validacoes.numero}
texto="Pelo menos 1 número"
/>
<RequisitoItem
atendido={validacoes.especial}
texto="Pelo menos 1 caractere especial"
/>
<RequisitoItem
atendido={validacoes.coincide}
texto="Senhas coincidem"
/>
</View>
<Button
title="Salvar Nova Senha"
onPress={() => salvarNovaSenha(senha)}
disabled={!senhaValida}
/>
</View>
);
}
function RequisitoItem({ atendido, texto }) {
return (
<View style={styles.requisito}>
<Icon
name={atendido ? 'check-circle' : 'circle'}
size={20}
color={atendido ? '#4CAF50' : '#999'}
/>
<Text style={[
styles.requisitoTexto,
atendido && styles.requisitoAtendido
]}>
{texto}
</Text>
</View>
);
}
⚠️ Tratamento de Erros
| Código | Descrição | Ação Recomendada |
|---|---|---|
| 400 | Dados inválidos | Verificar CPF e data de nascimento |
| 401 | Token expirado | Reiniciar fluxo de recuperação |
| 404 | Cliente não encontrado | Verificar CPF ou sugerir primeiro acesso |
| 410 | Código SMS expirado | Solicitar reenvio de código |
| 422 | Código SMS inválido | Permitir nova tentativa (máx 3) |
| 429 | Muitas tentativas | Aguardar 15 minutos |
Exemplo de Tratamento
function tratarErroRecuperacao(error) {
if (error.status === 404) {
return {
titulo: 'CPF não encontrado',
mensagem: 'Verifique se o CPF está correto ou faça seu primeiro acesso.',
acao: 'Ir para Primeiro Acesso'
};
}
if (error.status === 422) {
return {
titulo: 'Código incorreto',
mensagem: 'O código digitado está incorreto. Tente novamente.',
acao: 'Tentar Novamente'
};
}
if (error.status === 429) {
return {
titulo: 'Muitas tentativas',
mensagem: 'Por segurança, aguarde 15 minutos antes de tentar novamente.',
acao: 'Voltar'
};
}
return {
titulo: 'Erro',
mensagem: 'Não foi possível recuperar a senha. Tente novamente mais tarde.',
acao: 'Fechar'
};
}
🔒 Segurança
1. Limite de Tentativas
// Implementar limite de tentativas de recuperação
class GerenciadorRecuperacao {
constructor() {
this.tentativasPorCPF = new Map();
this.maxTentativas = 5;
this.periodoLimite = 24 * 60 * 60 * 1000; // 24 horas
}
async verificarLimite(cpf) {
const agora = Date.now();
const tentativas = this.tentativasPorCPF.get(cpf) || [];
// Remover tentativas antigas
const tentativasRecentes = tentativas.filter(
timestamp => (agora - timestamp) < this.periodoLimite
);
if (tentativasRecentes.length >= this.maxTentativas) {
throw new Error(
'Limite de tentativas excedido. Tente novamente em 24 horas.'
);
}
// Registrar nova tentativa
tentativasRecentes.push(agora);
this.tentativasPorCPF.set(cpf, tentativasRecentes);
}
}
2. Log de Auditoria
// Registrar todas as tentativas de recuperação
async function registrarTentativaRecuperacao(cpf, sucesso, motivo) {
await fetch(`${baseUrl}/auditoria/recuperacao-senha`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
cpf: cpf,
timestamp: new Date().toISOString(),
sucesso: sucesso,
motivo: motivo,
dispositivo: await obterDadosDispositivo()
})
});
}
🎓 Recursos Adicionais
- 🔧 Guia de Troubleshooting — Solução de problemas e erros comuns
- 📄 Referência OpenAPI — Especificação técnica completa
📚 Recursos Relacionados
- Login - Autenticação de usuários
- Primeiro Acesso - Cadastro de novos usuários
- Troca de Dispositivo - Autorizar novo dispositivo