1use serde::{Deserialize, Serialize};
2
3pub type Cents = i64;
4
5#[derive(Debug, thiserror::Error)]
6pub enum Error {
7 #[error("http: {0}")]
8 Http(#[from] reqwest::Error),
9 #[error("api error {code:?}: {message:?}")]
10 Api { code: Option<String>, message: Option<String> },
11 #[error("token verification failed")]
12 Token,
13 #[error("invalid receipt: {0}")]
14 Receipt(&'static str),
15}
16pub type Result<T> = std::result::Result<T, Error>;
17
18#[derive(Debug, Clone)]
19pub struct Config {
20 pub terminal_key: String,
21 pub password: String,
22 pub base: String,
24 pub user_agent: Option<String>,
25 pub timeout_ms: u64,
27 pub max_retries: usize,
29 pub initial_backoff_ms: u64,
30 pub idempotence_header: Option<String>,
32}
33impl Config {
34 pub fn new(terminal_key: impl Into<String>, password: impl Into<String>) -> Self {
35 Self {
36 terminal_key: terminal_key.into(),
37 password: password.into(),
38 base: "https://securepay.tinkoff.ru".into(),
39 user_agent: Some(format!("tinkoff-acquiring/{}", env!("CARGO_PKG_VERSION"))),
40 timeout_ms: 10_000,
41 max_retries: 3,
42 initial_backoff_ms: 250,
43 idempotence_header: Some("Idempotence-Key".into()),
44 }
45 }
46}
47
48#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
51#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
52pub enum Status {
53 New,
54 ThreeDsChecking,
55 ThreeDsChecked,
56 Authorized,
57 Confirmed,
58 Rejected,
59 Canceled,
60 Refund,
61 PartiallyRefunded,
62 DeadlineExpired,
63 AuthorizationDeclined,
64 Reversed,
65 FormShowed,
66 Unknown,
67}
68impl Default for Status { fn default() -> Self { Status::Unknown } }
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
71#[serde(untagged)]
72pub enum ErrorCodeEnum {
73 Known(KnownError),
74 Unknown(String),
75}
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
77#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
78pub enum KnownError {
79 #[serde(alias="0")] Ok,
80 #[serde(alias="7")] InvalidToken,
81 #[serde(alias="8")] PaymentNotFound,
82 #[serde(alias="9999")] Unknown,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
86#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
87pub enum FailureReason {
88 InsufficientFunds,
89 ExpiredCard,
90 IncorrectCvv,
91 BankDecline,
92 SuspectedFraud,
93 Unknown,
94}
95
96#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
97#[serde(rename_all = "lowercase")]
98pub enum Taxation { Osn, UsnIncome, UsnIncomeOutcome, Patent, Envd, Esn }
99
100#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
101#[serde(rename_all = "lowercase")]
102pub enum Tax { None, Vat0, Vat10, Vat20, Vat110, Vat120 }
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct Receipt {
108 #[serde(rename="Email", skip_serializing_if="Option::is_none")]
109 pub email: Option<String>,
110 #[serde(rename="Phone", skip_serializing_if="Option::is_none")]
111 pub phone: Option<String>,
112 #[serde(rename="Taxation")]
113 pub taxation: Taxation,
114 #[serde(rename="Items")]
115 pub items: Vec<Item>,
116 #[serde(skip)]
117 pub currency: Option<String>,
118}
119impl Receipt {
120 pub fn validate(&self) -> Result<()> {
121 if self.items.is_empty() { return Err(Error::Receipt("empty items")); }
122 for it in &self.items {
123 let expected = ((it.price as f64) * it.quantity).round() as i64;
125 if expected != it.amount {
126 return Err(Error::Receipt("item amount != price * quantity (rounded)"));
127 }
128 if it.price <= 0 || it.amount <= 0 {
129 return Err(Error::Receipt("non-positive price/amount"));
130 }
131 }
132 Ok(())
133 }
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct Item {
138 #[serde(rename="Name")]
139 pub name: String,
140 #[serde(rename="Price")]
141 pub price: Cents,
142 #[serde(rename="Quantity")]
143 pub quantity: f64,
144 #[serde(rename="Amount")]
145 pub amount: Cents,
146 #[serde(rename="Tax")]
147 pub tax: Tax,
148}
149
150#[derive(Debug, Serialize)]
153pub struct InitRequest<'a> {
154 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
155 #[serde(rename="Amount")] pub amount: Cents,
156 #[serde(rename="OrderId")] pub order_id: &'a str,
157 #[serde(rename="Description", skip_serializing_if="Option::is_none")] pub description: Option<&'a str>,
158 #[serde(rename="PayType", skip_serializing_if="Option::is_none")] pub pay_type: Option<&'a str>,
159 #[serde(rename="Receipt", skip_serializing_if="Option::is_none")] pub receipt: Option<serde_json::Value>,
160 #[serde(rename="DATA", skip_serializing_if="Option::is_none")] pub data: Option<serde_json::Value>,
161 #[serde(rename="SuccessURL", skip_serializing_if="Option::is_none")] pub success_url: Option<&'a str>,
162 #[serde(rename="FailURL", skip_serializing_if="Option::is_none")] pub fail_url: Option<&'a str>,
163 #[serde(rename="Recurrent", skip_serializing_if="Option::is_none")] pub recurrent: Option<String>,
164 #[serde(rename="Token")] pub token: String,
165}
166#[derive(Debug, Deserialize)]
167pub struct InitResponse {
168 #[serde(rename="Success")] pub success: bool,
169 #[serde(rename="PaymentId")] pub payment_id: Option<String>,
170 #[serde(rename="PaymentURL")]pub payment_url: Option<String>,
171 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
172 #[serde(rename="Message")] pub message: Option<String>,
173}
174
175#[derive(Debug, Serialize)]
176pub struct GetStateRequest<'a> {
177 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
178 #[serde(rename="PaymentId")] pub payment_id: &'a str,
179 #[serde(rename="Token")] pub token: String,
180}
181#[derive(Debug, Deserialize, Default)]
182pub struct GetStateResponse {
183 #[serde(rename="Success")] pub success: bool,
184 #[serde(rename="Status")] pub status: Option<Status>,
185 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
186 #[serde(rename="Message")] pub message: Option<String>,
187 #[serde(rename="FailureReason")] pub failure_reason: Option<FailureReason>,
188}
189
190#[derive(Debug, Serialize)]
191pub struct ConfirmRequest<'a> {
192 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
193 #[serde(rename="PaymentId")] pub payment_id: &'a str,
194 #[serde(rename="Amount", skip_serializing_if="Option::is_none")] pub amount: Option<Cents>,
195 #[serde(rename="Receipt", skip_serializing_if="Option::is_none")] pub receipt: Option<serde_json::Value>,
196 #[serde(rename="Token")] pub token: String,
197}
198#[derive(Debug, Deserialize)]
199pub struct ConfirmResponse {
200 #[serde(rename="Success")] pub success: bool,
201 #[serde(rename="Status")] pub status: Option<Status>,
202 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
203 #[serde(rename="Message")] pub message: Option<String>,
204}
205
206#[derive(Debug, Serialize)]
207pub struct CancelRequest<'a> {
208 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
209 #[serde(rename="PaymentId")] pub payment_id: &'a str,
210 #[serde(rename="Amount", skip_serializing_if="Option::is_none")] pub amount: Option<Cents>,
211 #[serde(rename="Receipt", skip_serializing_if="Option::is_none")] pub receipt: Option<serde_json::Value>,
212 #[serde(rename="Token")] pub token: String,
213}
214#[derive(Debug, Deserialize)]
215pub struct CancelResponse {
216 #[serde(rename="Success")] pub success: bool,
217 #[serde(rename="Status")] pub status: Option<Status>,
218 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
219 #[serde(rename="Message")] pub message: Option<String>,
220}
221
222#[derive(Debug, Serialize)]
224pub struct ChargeRequest<'a> {
225 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
226 #[serde(rename="PaymentId")] pub payment_id: &'a str,
227 #[serde(rename="Token")] pub token: String,
228}
229#[derive(Debug, Deserialize)]
230pub struct ChargeResponse {
231 #[serde(rename="Success")] pub success: bool,
232 #[serde(rename="Status")] pub status: Option<Status>,
233 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
234 #[serde(rename="Message")] pub message: Option<String>,
235}
236
237#[derive(Debug, Serialize)]
239pub struct GetCardListRequest<'a> {
240 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
241 #[serde(rename="CustomerKey")] pub customer_key: &'a str,
242 #[serde(rename="Token")] pub token: String,
243}
244#[derive(Debug, Deserialize)]
245pub struct GetCardListResponse {
246 #[serde(rename="Success")] pub success: bool,
247 #[serde(rename="Cards")] pub cards: Option<Vec<Card>>,
248 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
249 #[serde(rename="Message")] pub message: Option<String>,
250}
251#[derive(Debug, Deserialize)]
252pub struct Card {
253 #[serde(rename="Pan")] pub pan: Option<String>,
254 #[serde(rename="CardId")] pub card_id: Option<String>,
255 #[serde(rename="Status")] pub status: Option<String>,
256 #[serde(rename="RebillId")]pub rebill_id: Option<String>,
257}
258#[derive(Debug, Serialize)]
259pub struct RemoveCardRequest<'a> {
260 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
261 #[serde(rename="CustomerKey")] pub customer_key: &'a str,
262 #[serde(rename="CardId")] pub card_id: &'a str,
263 #[serde(rename="Token")] pub token: String,
264}
265#[derive(Debug, Deserialize)]
266pub struct RemoveCardResponse {
267 #[serde(rename="Success")] pub success: bool,
268 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
269 #[serde(rename="Message")] pub message: Option<String>,
270}
271
272#[derive(Debug, Serialize)]
274pub struct AddCustomerRequest<'a> {
275 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
276 #[serde(rename="CustomerKey")] pub customer_key: &'a str,
277 #[serde(rename="Email", skip_serializing_if="Option::is_none")] pub email: Option<&'a str>,
278 #[serde(rename="Phone", skip_serializing_if="Option::is_none")] pub phone: Option<&'a str>,
279 #[serde(rename="Token")] pub token: String,
280}
281#[derive(Debug, Deserialize)]
282pub struct AddCustomerResponse {
283 #[serde(rename="Success")] pub success: bool,
284 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
285 #[serde(rename="Message")] pub message: Option<String>,
286}
287#[derive(Debug, Serialize)]
288pub struct GetCustomerRequest<'a> {
289 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
290 #[serde(rename="CustomerKey")] pub customer_key: &'a str,
291 #[serde(rename="Token")] pub token: String,
292}
293#[derive(Debug, Deserialize)]
294pub struct GetCustomerResponse {
295 #[serde(rename="Success")] pub success: bool,
296 #[serde(rename="CustomerKey")] pub customer_key: Option<String>,
297 #[serde(rename="Email")] pub email: Option<String>,
298 #[serde(rename="Phone")] pub phone: Option<String>,
299 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
300 #[serde(rename="Message")] pub message: Option<String>,
301}
302#[derive(Debug, Serialize)]
303pub struct RemoveCustomerRequest<'a> {
304 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
305 #[serde(rename="CustomerKey")] pub customer_key: &'a str,
306 #[serde(rename="Token")] pub token: String,
307}
308#[derive(Debug, Deserialize)]
309pub struct RemoveCustomerResponse {
310 #[serde(rename="Success")] pub success: bool,
311 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
312 #[serde(rename="Message")] pub message: Option<String>,
313}
314
315#[derive(Debug, Serialize)]
317pub struct BindCardRequest<'a> {
318 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
319 #[serde(rename="CustomerKey")] pub customer_key: &'a str,
320 #[serde(rename="Token")] pub token: String,
321}
322#[derive(Debug, Deserialize)]
323pub struct BindCardResponse {
324 #[serde(rename="Success")] pub success: bool,
325 #[serde(rename="RequestKey")] pub request_key: Option<String>,
326 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
327 #[serde(rename="Message")] pub message: Option<String>,
328}
329
330#[derive(Debug, Serialize)]
331pub struct FinishAuthorizeRequest<'a> {
332 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
333 #[serde(rename="PaymentId")] pub payment_id: &'a str,
334 #[serde(rename="DATA")] pub data: serde_json::Value,
335 #[serde(rename="Token")] pub token: String,
336}
337#[derive(Debug, Deserialize)]
338pub struct FinishAuthorizeResponse {
339 #[serde(rename="Success")] pub success: bool,
340 #[serde(rename="Status")] pub status: Option<Status>,
341 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
342 #[serde(rename="Message")] pub message: Option<String>,
343}
344
345#[derive(Debug, Serialize)]
347pub struct GetQrRequest<'a> {
348 #[serde(rename="TerminalKey")] pub terminal_key: &'a str,
349 #[serde(rename="PaymentId")] pub payment_id: &'a str,
350 #[serde(rename="Token")] pub token: String,
351}
352#[derive(Debug, Deserialize)]
353pub struct GetQrResponse {
354 #[serde(rename="Success")] pub success: bool,
355 #[serde(rename="Data")] pub data: Option<String>,
356 #[serde(rename="ErrorCode")] pub error_code: Option<String>,
357 #[serde(rename="Message")] pub message: Option<String>,
358}