post method
- String endpoint,
- BaseRequest request, {
- String? contratanteNumero,
- String? autorPedidoDadosNumero,
- String? procuradorToken,
Executa uma requisição POST para a API do SERPRO
endpoint: Caminho do endpoint (ex: '/Ccmei/Emitir')
request: Objeto BaseRequest contendo os dados da requisição
contratanteNumero: CNPJ do contratante (opcional, usa o padrão se não fornecido)
autorPedidoDadosNumero: CPF/CNPJ do autor (opcional, usa o padrão se não fornecido)
procuradorToken: Token de procurador para operações que requerem procuração
Retorna: Map com a resposta da API ou lança exceção em caso de erro
Renovação Automática de Tokens
Este método verifica automaticamente se o token está próximo de expirar e renova antes de fazer a requisição. Isso é transparente para o usuário.
Implementation
Future<Map<String, dynamic>> post(
String endpoint,
BaseRequest request, {
String? contratanteNumero,
String? autorPedidoDadosNumero,
String? procuradorToken,
}) async {
// Verificar se o cliente foi autenticado
if (_authModel == null) {
throw _buildErrorResponse(
mensagem: 'Cliente não autenticado',
status: 401,
resposta: 'Primeiro faça a autenticação usando o método authenticate()',
);
}
// RENOVAÇÃO AUTOMÁTICA: fluxo mTLS preenche [_storedCredentials]; autenticação via
// Cloud Function (Web) não — nesse caso [shouldRefresh] não pode forçar renovação mTLS.
// Caso contrário ocorre falso "Token expirado" logo após authenticateWithProcurador.
final podeRenovarComMtls =
_storedCredentials != null && _authService != null;
final precisaRenovarComMtls =
(_authModel!.shouldRefresh || _authModel!.isExpired) &&
podeRenovarComMtls;
final tokenExpiradoSemRenovacao =
_authModel!.isExpired && !podeRenovarComMtls;
if (precisaRenovarComMtls) {
try {
_authModel = await _authService!.authenticate(_storedCredentials!);
} catch (e) {
_authModel = null;
throw Exception(
'Token expirado e não foi possível renovar automaticamente. '
'Erro: $e. Por favor, chame authenticate() novamente.',
);
}
} else if (tokenExpiradoSemRenovacao) {
_authModel = null;
throw Exception(
'Token expirado. Por favor, chame authenticate() novamente.',
);
}
// Usar dados customizados se fornecidos, senão usar os dados padrão
final finalContratanteNumero =
contratanteNumero ?? _authModel!.contratanteNumero;
// Quando há token de procurador, o autorPedidoDadosNumero deve ser o contratante da solução
final finalAutorPedidoDadosNumero =
autorPedidoDadosNumero ??
(hasProcuradorToken
? _authModel!.contratanteNumero
: _authModel!.autorPedidoDadosNumero);
// Criar o JSON completo usando os dados de autenticação
final requestBody = request.toJsonWithAuth(
contratanteNumero: finalContratanteNumero,
contratanteTipo: ValidacoesUtils.detectDocumentType(
finalContratanteNumero,
),
autorPedidoDadosNumero: finalAutorPedidoDadosNumero,
autorPedidoDadosTipo: ValidacoesUtils.detectDocumentType(
finalAutorPedidoDadosNumero,
),
);
// Preparar headers obrigatórios
final headers = <String, String>{
'Authorization': 'Bearer ${_authModel!.accessToken}',
'jwt_token': _authModel!.jwtToken,
'Content-Type': 'application/json',
};
// Adicionar token de procurador (sempre do authModel, parâmetro ignorado)
if (_authModel != null && _authModel!.procuradorToken.isNotEmpty) {
headers['autenticar_procurador_token'] = _authModel!.procuradorToken;
}
// Gerar e adicionar identificador de requisição
final requestTag = RequestTagGenerator.generateRequestTag(
autorPedidoDadosNumero: finalAutorPedidoDadosNumero,
contribuinteNumero: request.contribuinteNumero,
idServico: request.pedidoDados.idServico,
);
headers['X-Request-Tag'] = requestTag;
// Executar requisição HTTP POST
final response = await (() async {
// Se urlProxy estiver configurado (Web), usar proxy
if (_urlProxy != null && _urlProxy!.isNotEmpty) {
final proxyUrl = Uri.parse('$_urlProxy/proxy_serpro');
final proxyHeaders = <String, String>{
'Content-Type': 'application/json',
};
final proxyBody = {
'endpoint': endpoint,
'body': requestBody,
'access_token': _authModel!.accessToken,
'jwt_token': _authModel!.jwtToken,
'procurador_token': _authModel!.procuradorToken.isNotEmpty
? _authModel!.procuradorToken
: null,
'ambiente': _ambiente,
'certificado_base64':
_cloudFunctionCertBase64 ?? _storedCredentials?.certBase64,
'certificado_senha':
_cloudFunctionCertPassword ?? _storedCredentials?.certPassword,
};
//print("================================================");
//print("Usando proxy: $proxyUrl");
//print("Proxy body: ${json.encode(proxyBody)}");
//print("================================================");
return await http.post(
proxyUrl,
headers: proxyHeaders,
body: json.encode(proxyBody),
);
} else {
// Requisição direta (Desktop/Mobile ou quando urlProxy não está configurado)
//print("================================================");
//print("Endpoint direto: $_baseUrl$endpoint");
//print("headers: ${headers}");
//print("requestBody: ${json.encode(requestBody)}");
//print("================================================");
return await http.post(
Uri.parse('$_baseUrl$endpoint'),
headers: headers,
body: json.encode(requestBody),
);
}
})();
// Verificar se a requisição foi bem-sucedida
if (response.statusCode >= 200 && response.statusCode < 300) {
Map<String, dynamic> responseBody =
json.decode(utf8.decode(response.bodyBytes)) as Map<String, dynamic>;
// Verificar se a API retornou um erro de negócio
if (responseBody.isNotEmpty &&
responseBody['mensagens'] != null &&
responseBody['mensagens'] is List &&
(responseBody['mensagens'] as List).isNotEmpty &&
responseBody['mensagens'][0]['codigo'] == "ERRO") {
// Reformatar resposta de erro
responseBody = {
"rota": endpoint,
"status": response.statusCode,
"idSistema": requestBody['pedidoDados']['idSistema'],
"idServico": requestBody['pedidoDados']['idServico'],
"mensagens": "${responseBody['mensagens'][0]['texto']}",
"body": json.encode(requestBody),
};
throw Exception(responseBody);
}
return responseBody;
} else if (response.statusCode == 401) {
throw Exception({
"status": response.statusCode,
"mensagens":
"Credenciais inválidas. Certifique-se de ter fornecido as credenciais de segurança corretas",
"body": "Credenciais inválidas",
});
} else if (response.statusCode == 304) {
final autenticarProcuradorToken = response.headers['etag']
.toString()
.replaceAll(':', '":"');
final expiresISO =
FormatadorUtils.converterHttpExpiresParaISO(
response.headers['expires'],
) ??
'';
final stringBody =
"{$autenticarProcuradorToken, \"data_hora_expiracao\":\"$expiresISO\"}";
final body = jsonDecode(stringBody);
return {
"status": response.statusCode,
"mensagens": "Resposta em cache (304 Not Modified)",
"dados": body,
};
} else {
throw Exception(
'Falha na requisição: ${response.statusCode} - ${utf8.decode(response.bodyBytes)}',
);
}
}