fns_api_client/client/
mod.rs1use std::sync::Arc;
2
3use chrono::{NaiveDateTime, ParseError};
4use reqwest::Client;
5use tokio::time::{Duration, sleep};
6use yaserde::ser::to_string;
7use log::debug;
8use serde_json::from_str;
9
10use crate::client::error::{FnsApiError, HttpClientError, OpenApiClientError};
11use crate::dto::ticket_request;
12use crate::dto::ticket_response::Envelope;
13use crate::models::auth_response::{AuthResponse, AuthResponseResult, AuthResponseToken};
14use crate::models::message_response::{MessageResponse, MessageStatus};
15use crate::models::ticket::TicketResponse;
16use crate::traits::check_query::CheckQueryTrait;
17use crate::traits::ticket::{Ticket, TicketResponseResult, TicketResponseTrait, TicketTrait};
18
19pub mod error;
20
21pub struct OpenApiClient {
22 http_client: Client,
23 master_token: String,
24 user_token: String,
25 temp_token: Option<AuthResponseToken>,
26}
27
28impl OpenApiClient {
29 pub fn new(master_token: &String, user_token: &String) -> Self {
30 OpenApiClient {
31 http_client: Client::new(),
32 master_token: master_token.to_string(),
33 user_token: user_token.to_string(),
34 temp_token: None,
35 }
36 }
37 pub async fn authorize(&mut self) -> Result<AuthResponse, OpenApiClientError> {
38 debug!("Авторизация");
39 let model = crate::models::auth_request::AuthRequest::new(&self.master_token);
40 let query = self.http_client
41 .post("https://openapi.nalog.ru:8090/open-api/AuthService/0.1")
42 .body(model.to_string());
43 let auth_request = query
44 .send()
45 .await;
46 match auth_request {
47 Ok(response) => {
48 match response.text().await {
49 Ok(xml_string) => {
50 let auth_response = AuthResponse::new(&xml_string);
51 match auth_response.clone() {
52 Ok(res) => {
53 match res.result {
54 AuthResponseResult::Ok(token) => {
55 self.temp_token = Some(token);
56 debug!("Токен получен");
57 }
58 _ => {}
59 }
60 }
61 _ => {}
62 };
63 auth_response
64 }
65 Err(error) => {
66 Err(OpenApiClientError::FnsApiError(FnsApiError { message: error.to_string() }))
67 }
68 }
69 }
70 Err(error) => {
71 Err(OpenApiClientError::HttpClientError(HttpClientError { message: error.to_string() }))
72 }
73 }
74 }
75 pub async fn get_ticket_with_retry(&self, check_query: impl CheckQueryTrait + Clone) -> Result<Arc<crate::dto::ticket::Ticket>, OpenApiClientError> {
76 let mut attempt = 0;
77 let max_attempts = 5;
78 let base_delay = Duration::from_secs(1);
79
80 while attempt < max_attempts {
81 match self.get_ticket(check_query.clone()).await {
82 Ok(ticket) => {
83 debug!("Ответ {:?}", ticket);
84 let status = match ticket.status() {
85 Some(status) => status,
86 None => return Err(OpenApiClientError::Error(String::from("Ошибка получения статуса"))),
87 };
88 debug!("Статус {:?}", status);
89 match status {
90 MessageStatus::Complete => {
91 let json_string = ticket.ticket_json();
92 debug!("{}", json_string.clone());
93 let ticket_json = from_str::<crate::dto::ticket::Ticket>(json_string.as_str()).unwrap();
94 return Ok(Arc::new(ticket_json))
95 }
96 MessageStatus::Processing => {
97 let delay = base_delay * 2u32.pow(attempt);
98 println!("Retry attempt {}: waiting for {:?} before next retry", attempt + 1, delay);
99 sleep(delay).await;
100 attempt += 1;
101 }
102 MessageStatus::Unknown => {
103 return Err(OpenApiClientError::Error("Неизвестный статус сообщения".to_string()))
104 }
105 }
106 },
107 Err(e) => {
108 return Err(e)
109 }
110 }
111 }
112 Err(OpenApiClientError::Error(String::from("Failed after max retry attempts")))
113 }
114 pub async fn get_ticket(&self, check_query: impl CheckQueryTrait) -> Result<crate::dto::messages_response::Envelope, OpenApiClientError> {
115 debug!("Получение чека");
116 let ticket_response = self.get_ticket_response(check_query).await;
117 let temp_token = match &self.temp_token {
118 Some(token) => token,
119 None => return Err(OpenApiClientError::Error(String::from("Отсутствует FNS-OpenApi-UserToken"))),
120 };
121 match ticket_response {
122 Ok(ticket_response) => {
123 match ticket_response.result() {
124 TicketResponseResult::Ok(message) => {
125 debug!("Сообщение {:?}", message);
126 let response = MessageResponse::new(Arc::new(self.http_client.clone()), Arc::new(self.user_token.clone()), Arc::new(temp_token.clone()))
127 .send(message).await;
128 response
129 }
130 TicketResponseResult::Err(err) => {
131 Err(OpenApiClientError::FnsApiError(FnsApiError { message: err.message() }))
132 }
133 }
134 }
135 Err(error) => {
136 Err(error)
137 }
138 }
139 }
140 async fn get_ticket_response(&self, check_query: impl CheckQueryTrait) -> Result<Arc<dyn TicketResponseTrait>, OpenApiClientError> {
141 match &self.temp_token {
142 None => {
143 Err(OpenApiClientError::Error(String::from("Отсутствует FNS-OpenApi-UserToken")))
144 }
145 Some(temp_token) => {
146 fn convert_datetime(datetime: String) -> Result<String, ParseError> {
147 if let Ok(parsed_date) = NaiveDateTime::parse_from_str(&datetime, "%Y%m%dT%H%M") {
148 return Ok(parsed_date.format("%Y-%m-%dT%H:%M:%S").to_string());
149 }
150 let parsed_date = NaiveDateTime::parse_from_str(&datetime, "%Y%m%dT%H%M%S")?;
151 Ok(parsed_date.format("%Y-%m-%dT%H:%M:%S").to_string())
152 }
153
154 let ticket_request = ticket_request::Envelope {
155 body: ticket_request::Body {
156 send_message_request: ticket_request::SendMessageRequest {
157 message: ticket_request::Message {
158 get_ticket_request: ticket_request::GetTicketRequest {
159 get_ticket_info: ticket_request::GetTicketInfo {
160 sum: check_query.s(),
161 date: convert_datetime(check_query.t()).unwrap(),
162 r#fn: check_query.r#fn(),
163 type_operation: check_query.n(),
164 fiscal_document_id: check_query.i(),
165 fiscal_sign: check_query.fp(),
166 }
167 }
168 }
169 }
170 }
171 };
172 let body = to_string(&ticket_request).unwrap();
173 debug!("Отправка запроса на получение чека");
174 println!("{}", body.clone());
175 let ticket_info_query = self.http_client.post("https://openapi.nalog.ru:8090/open-api/ais3/KktService/0.1")
176 .body(body)
177 .header("FNS-OpenApi-Token", &temp_token.value)
178 .header("FNS-OpenApi-UserToken", &self.user_token);
179 let ticket_info_response = ticket_info_query.send()
180 .await;
181 match ticket_info_response {
182 Ok(response) => {
183 match response.text().await {
184 Ok(text) => {
185 let deserialize = crate::models::serde::Serde::from_xml::<Envelope>(&text);
186 match deserialize {
187 Ok(dto) => {
188 debug!("Ответ получен");
189 debug!("{:?}", dto);
190 debug!("{}", text);
191 TicketResponse::new(dto)
192 }
193 Err(xml_deserialization_error) => {
194 Err(OpenApiClientError::DeserializationError(xml_deserialization_error))
195 }
196 }
197 }
198 Err(error) => {
199 Err(OpenApiClientError::FnsApiError(FnsApiError { message: error.to_string() }))
200 }
201 }
202 }
203 Err(error) => {
204 Err(OpenApiClientError::HttpClientError(HttpClientError::new(error)))
205 }
206 }
207 }
208 }
209 }
210}