1use std::{
2 fs::File,
3 io::Read,
4 time::{Duration, Instant},
5};
6
7use reqwest::Error;
8use serde::{Deserialize, Serialize};
9use tokio::runtime::Runtime;
10
11#[derive(Deserialize, Serialize, Debug)]
12struct AuthPost {
13 grant_type: String,
14}
15
16#[derive(Deserialize, Serialize, Debug)]
17struct AuthResult {
18 access_token: String,
19 token_type: String,
20 expires_in: u32,
21 scope: String,
22}
23
24pub struct Gerencianet {
25 url: String,
26 cert: String,
27 client_id: String,
28 client_secret: String,
29 rt: Runtime,
30}
31
32#[derive(Debug)]
33pub struct Sessao {
34 access_token: String,
35 token_type: String,
36 expires_in: u32,
37 scope: String,
38}
39
40#[derive(Debug)]
41pub struct Cliente {
42 url: String,
43 cliente_req: reqwest::Client,
44 client_id: String,
45 client_secret: String,
46 expires_time: Instant,
47 rt: Runtime,
48 sessao: Sessao,
49}
50
51fn verificar_expiracao_token(cliente: &mut Cliente) {
52 if cliente.expires_time.elapsed() >= Duration::from_secs(cliente.sessao.expires_in.into()) {
53 cliente.refresh_token();
54 }
55}
56
57impl Cliente {
58 pub fn refresh_token(&mut self) -> &mut Self {
59 let resp = self.rt.block_on(async {
60 self.cliente_req
61 .post(format!("{}/oauth/token", self.url))
62 .basic_auth(
63 self.client_id.to_owned(),
64 Some(self.client_secret.to_owned()),
65 )
66 .json(&AuthPost {
67 grant_type: "client_credentials".to_owned(),
68 })
69 .send()
70 .await
71 .unwrap()
72 .json::<AuthResult>()
73 .await
74 .unwrap()
75 });
76 self.expires_time = Instant::now();
77 self.sessao = Sessao {
78 access_token: resp.access_token,
79 token_type: resp.token_type,
80 expires_in: resp.expires_in,
81 scope: resp.scope,
82 };
83 self
84 }
85}
86
87impl Default for Gerencianet {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93impl Gerencianet {
94 pub fn new() -> Self {
95 Self {
96 cert: String::new(),
97 rt: tokio::runtime::Runtime::new().unwrap(),
98 client_id: String::new(),
99 client_secret: String::new(),
100 url: "https://api-pix-h.gerencianet.com.br".to_owned(),
101 }
102 }
103 pub fn producao(&mut self) -> &mut Self {
104 self.url = "https://api-pix.gerencianet.com.br".to_owned();
105 self
106 }
107 pub fn homologacao(&mut self) -> &mut Self {
108 self.url = "https://api-pix-h.gerencianet.com.br".to_owned();
109 self
110 }
111 pub fn certificado_p12(&mut self, cert: String) -> &mut Self {
112 self.cert = cert;
113 self
114 }
115 pub fn basic_auth(&mut self, client_id: String, client_secret: String) -> &mut Self {
116 self.client_id = client_id;
117 self.client_secret = client_secret;
118 self
119 }
120 pub fn build(&self) -> Result<Cliente, Error> {
121 let resp = self.rt.block_on(async {
122 let mut buf = Vec::new();
123 File::open(&self.cert)
124 .unwrap()
125 .read_to_end(&mut buf)
126 .unwrap();
127
128 let identity = reqwest::Identity::from_pkcs12_der(&buf, "").unwrap();
129
130 let cliente_req = reqwest::Client::builder()
131 .identity(identity)
132 .danger_accept_invalid_certs(true)
133 .build()
134 .unwrap();
135 let auth = cliente_req
136 .post(format!("{}/oauth/token", self.url))
137 .basic_auth(&self.client_id, Some(&self.client_secret))
138 .json(&AuthPost {
139 grant_type: "client_credentials".to_owned(),
140 })
141 .send()
142 .await?
143 .json::<AuthResult>()
144 .await?;
145
146 Ok((cliente_req, auth))
147 });
148
149 match resp {
150 Ok(v) => {
151 let (cliente_req, sessao) = v;
152 Ok(Cliente {
153 url: self.url.to_owned(),
154 cliente_req,
155 client_id: self.client_id.to_owned(),
156 client_secret: self.client_secret.to_owned(),
157 rt: tokio::runtime::Runtime::new().unwrap(),
158 expires_time: Instant::now(),
159 sessao: Sessao {
160 access_token: sessao.access_token,
161 token_type: sessao.token_type,
162 expires_in: sessao.expires_in,
163 scope: sessao.scope,
164 },
165 })
166 }
167 Err(e) => Err(e),
168 }
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn teste() {
178 let result = "OK";
179 assert_eq!(result, "OK");
180 }
181}