tgbot/types/payment/invoice/mod.rs
1use serde::{Deserialize, Serialize};
2use serde_json::Error as JsonError;
3
4use crate::{
5 api::{Method, Payload},
6 types::{ChatId, InlineKeyboardMarkup, Integer, Message, ReplyParameters},
7};
8
9#[cfg(test)]
10mod tests;
11
12/// Represents an invoice.
13#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
14pub struct Invoice {
15 /// Three-letter ISO 4217 currency code.
16 pub currency: String,
17 /// Product description.
18 pub description: String,
19 /// Unique bot deep-linking parameter that can be used to generate this invoice.
20 pub start_parameter: String,
21 /// Product name.
22 pub title: String,
23 /// Total price in the smallest units of the currency (integer, not float/double).
24 ///
25 /// For example, for a price of US$ 1.45 pass amount = 145.
26 /// See the exp parameter in [currencies.json][1], it shows the number of digits past
27 /// the decimal point for each currency (2 for the majority of currencies).
28 ///
29 /// [1]: https://core.telegram.org/bots/payments/currencies.json
30 pub total_amount: Integer,
31}
32
33impl Invoice {
34 /// Creates a new `Invoice`.
35 ///
36 /// # Arguments
37 ///
38 /// * `currency` - ISO 4217 currency code.
39 /// * `description` - Product description.
40 /// * `start_parameter` - Unique bot deep-linking parameter.
41 /// * `title` - Product name.
42 /// * `total_amount` - Total price.
43 pub fn new<A, B, C, D>(currency: A, description: B, start_parameter: C, title: D, total_amount: Integer) -> Self
44 where
45 A: Into<String>,
46 B: Into<String>,
47 C: Into<String>,
48 D: Into<String>,
49 {
50 Self {
51 currency: currency.into(),
52 description: description.into(),
53 start_parameter: start_parameter.into(),
54 title: title.into(),
55 total_amount,
56 }
57 }
58}
59
60/// Represents a portion of the price for goods or services.
61#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
62pub struct LabeledPrice {
63 amount: Integer,
64 label: String,
65}
66
67impl LabeledPrice {
68 /// Creates a new `LabeledPrice`.
69 ///
70 /// # Arguments
71 ///
72 /// * `amount` - Price of the product in the smallest units of the currency.
73 /// * `label` - Portion label.
74 pub fn new<T>(amount: Integer, label: T) -> Self
75 where
76 T: Into<String>,
77 {
78 Self {
79 amount,
80 label: label.into(),
81 }
82 }
83
84 /// Returns the amount.
85 pub fn amount(&self) -> Integer {
86 self.amount
87 }
88
89 /// Returns the portion label.
90 pub fn label(&self) -> &str {
91 &self.label
92 }
93}
94
95/// Represents an invoice parameters used in [`CreateInvoiceLink`] and [`SendInvoice`].
96#[serde_with::skip_serializing_none]
97#[derive(Clone, Debug, Default, Deserialize, PartialEq, PartialOrd, Serialize)]
98pub struct InvoiceParameters {
99 /// Indicates whether the final price depends on the shipping method.
100 pub is_flexible: Option<bool>,
101 /// The maximum accepted amount for tips in the smallest units of the currency.
102 ///
103 /// For example, for a maximum tip of US$ 1.45 pass max_tip_amount = 145.
104 /// See the exp parameter in [currencies.json][1],
105 /// it shows the number of digits past the decimal point for each currency
106 /// (2 for the majority of currencies).
107 ///
108 /// Defaults to 0.
109 ///
110 /// [1]: (https://core.telegram.org/bots/payments/currencies.json)
111 pub max_tip_amount: Option<Integer>,
112 /// Indicates whether the user's email address is required to complete the order.
113 pub need_email: Option<bool>,
114 /// Indicates whether the user's full name is required to complete the order.
115 pub need_name: Option<bool>,
116 /// Indicates whether the user's phone number is required to complete the order.
117 pub need_phone_number: Option<bool>,
118 /// Indicates whether the user's shipping address is required to complete the order.
119 pub need_shipping_address: Option<bool>,
120 /// Photo height.
121 pub photo_height: Option<Integer>,
122 /// Photo size in bytes.
123 pub photo_size: Option<Integer>,
124 /// URL of the product photo for the invoice.
125 ///
126 /// Can be a photo of the goods or a marketing image for a service.
127 pub photo_url: Option<String>,
128 /// Photo width.
129 pub photo_width: Option<Integer>,
130 /// Data about the invoice, which will be shared with the payment provider.
131 ///
132 /// A detailed description of required fields should be provided by the payment provider.
133 pub provider_data: Option<String>,
134 /// Payment provider token, obtained via @BotFather.
135 ///
136 /// Pass an empty string for payments in Telegram Stars.
137 pub provider_token: Option<String>,
138 /// Indicates whether the user's phone number should be sent to the provider.
139 pub send_phone_number_to_provider: Option<bool>,
140 /// Indicates whether the user's email address should be sent to the provider.
141 pub send_email_to_provider: Option<bool>,
142 /// An array of suggested amounts of tips in the smallest units of the currency.
143 ///
144 /// At most 4 suggested tip amounts can be specified.
145 /// The suggested tip amounts must be positive,
146 /// passed in a strictly increased order and must not exceed `max_tip_amount`.
147 pub suggested_tip_amounts: Option<Vec<Integer>>,
148}
149
150impl InvoiceParameters {
151 /// Sets a new value for the `is_flexible` flag.
152 ///
153 /// # Arguments
154 ///
155 /// * `value` - Indicates whether the final price depends on the shipping method.
156 pub fn with_flexible(mut self, value: bool) -> Self {
157 self.is_flexible = Some(value);
158 self
159 }
160
161 /// Sets a new max tip amount.
162 ///
163 /// # Arguments
164 ///
165 /// * `value` - The maximum accepted amount for tips in the smallest units of the currency.
166 pub fn with_max_tip_amount(mut self, value: Integer) -> Self {
167 self.max_tip_amount = Some(value);
168 self
169 }
170
171 /// Sets a new value for the `need_email` flag.
172 ///
173 /// # Arguments
174 ///
175 /// * `value` - Indicates whether the user's email address is required to complete the order.
176 pub fn with_need_email(mut self, value: bool) -> Self {
177 self.need_email = Some(value);
178 self
179 }
180
181 /// Sets a new value for the `need_name` flag.
182 ///
183 /// # Arguments
184 ///
185 /// * `value` - Indicates whether the user's full name is required to complete the order.
186 pub fn with_need_name(mut self, value: bool) -> Self {
187 self.need_name = Some(value);
188 self
189 }
190
191 /// Sets a new value for the `need_phone_number` flag.
192 ///
193 /// # Arguments
194 ///
195 /// * `value` - Indicates whether the user's phone number is required to complete the order.
196 pub fn with_need_phone_number(mut self, value: bool) -> Self {
197 self.need_phone_number = Some(value);
198 self
199 }
200
201 /// Sets a new value for the `need_shipping_address` flag.
202 ///
203 /// # Arguments
204 ///
205 /// * `value` - Indicates whether the user's shipping address is required to complete the order.
206 pub fn with_need_shipping_address(mut self, value: bool) -> Self {
207 self.need_shipping_address = Some(value);
208 self
209 }
210
211 /// Sets a new photo height.
212 ///
213 /// # Arguments
214 ///
215 /// * `value` - Photo height.
216 pub fn with_photo_height(mut self, value: Integer) -> Self {
217 self.photo_height = Some(value);
218 self
219 }
220
221 /// Sets a new photo size.
222 ///
223 /// # Arguments
224 ///
225 /// * `value` - Photo size in bytes.
226 pub fn with_photo_size(mut self, value: Integer) -> Self {
227 self.photo_size = Some(value);
228 self
229 }
230
231 /// Sets a new photo URL.
232 ///
233 /// # Arguments
234 ///
235 /// * `value` - Photo URL.
236 pub fn with_photo_url<T>(mut self, value: T) -> Self
237 where
238 T: Into<String>,
239 {
240 self.photo_url = Some(value.into());
241 self
242 }
243
244 /// Sets a new photo width.
245 ///
246 /// # Arguments
247 ///
248 /// * `value` - Photo width.
249 pub fn with_photo_width(mut self, value: Integer) -> Self {
250 self.photo_width = Some(value);
251 self
252 }
253
254 /// Sets a new provider data.
255 ///
256 /// # Arguments
257 ///
258 /// * `value` - Data about the invoice, which will be shared with the payment provider.
259 pub fn with_provider_data<T>(mut self, value: &T) -> Result<Self, JsonError>
260 where
261 T: Serialize,
262 {
263 self.provider_data = Some(serde_json::to_string(value)?);
264 Ok(self)
265 }
266
267 /// Sets a new provider token.
268 ///
269 /// # Arguments
270 ///
271 /// * `value` - Payment provider token, obtained via @BotFather.
272 /// Pass an empty string for payments in Telegram Stars.
273 pub fn with_provider_token<T>(mut self, value: T) -> Self
274 where
275 T: Into<String>,
276 {
277 self.provider_token = Some(value.into());
278 self
279 }
280
281 /// Sets a new value for the `send_phone_number_to_provider` flag.
282 ///
283 /// # Arguments
284 ///
285 /// * `value` - Indicates whether the user's phone number should be sent to the provider.
286 pub fn with_send_phone_number_to_provider(mut self, value: bool) -> Self {
287 self.send_phone_number_to_provider = Some(value);
288 self
289 }
290
291 /// Sets a new value for the `send_email_to_provider` flag.
292 ///
293 /// # Arguments
294 ///
295 /// * `value` - Indicates whether the user's email address should be sent to the provider.
296 pub fn with_send_email_to_provider(mut self, value: bool) -> Self {
297 self.send_email_to_provider = Some(value);
298 self
299 }
300
301 /// Sets a new list of max tip amounts.
302 ///
303 /// # Arguments
304 ///
305 /// * `value` - An array of suggested amounts of tips in the smallest units of the currency.
306 pub fn with_suggested_tip_amounts<T>(mut self, value: T) -> Self
307 where
308 T: IntoIterator<Item = Integer>,
309 {
310 self.suggested_tip_amounts = Some(value.into_iter().collect());
311 self
312 }
313}
314
315/// Creates a link for an invoice.
316///
317/// Returns the created invoice link as String on success.
318#[serde_with::skip_serializing_none]
319#[derive(Clone, Debug, Serialize)]
320pub struct CreateInvoiceLink {
321 currency: String,
322 description: String,
323 payload: String,
324 prices: Vec<LabeledPrice>,
325 title: String,
326 business_connection_id: Option<String>,
327 subscription_period: Option<Integer>,
328 #[serde(flatten)]
329 parameters: Option<InvoiceParameters>,
330}
331
332impl CreateInvoiceLink {
333 /// Creates a new `CreateInvoiceLink`.
334 ///
335 /// # Arguments
336 ///
337 /// * `title` - Product name; 1-32 characters.
338 /// * `description` - Product description; 1-255 characters.
339 /// * `payload` - Bot-defined invoice payload; 1-128 bytes;
340 /// this will not be displayed to the user;
341 /// use for your internal processes.
342 /// * `currency` - Three-letter ISO 4217 currency code, see more on currencies.
343 /// * `prices` - Price breakdown
344 /// (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.).
345 pub fn new<A, B, C, D, E>(title: A, description: B, payload: C, currency: D, prices: E) -> Self
346 where
347 A: Into<String>,
348 B: Into<String>,
349 C: Into<String>,
350 D: Into<String>,
351 E: IntoIterator<Item = LabeledPrice>,
352 {
353 Self {
354 currency: currency.into(),
355 description: description.into(),
356 payload: payload.into(),
357 prices: prices.into_iter().collect(),
358 title: title.into(),
359 business_connection_id: None,
360 subscription_period: None,
361 parameters: None,
362 }
363 }
364
365 /// Sets a new business connection ID.
366 ///
367 /// # Arguments
368 ///
369 /// * `value` - Unique identifier of the business connection on behalf of which the link will be created.
370 pub fn with_business_connection_id<T>(mut self, value: T) -> Self
371 where
372 T: Into<String>,
373 {
374 self.business_connection_id = Some(value.into());
375 self
376 }
377
378 /// Sets a new invoice parameters.
379 ///
380 /// # Arguments
381 ///
382 /// * `value` - Invoice parameters.
383 pub fn with_parameters(mut self, value: InvoiceParameters) -> Self {
384 self.parameters = Some(value);
385 self
386 }
387
388 /// Sets a new subscription period.
389 ///
390 /// # Arguments
391 ///
392 /// * `value` - The number of seconds the subscription will be active for before the next payment.
393 /// The currency must be set to “XTR” (Telegram Stars) if the parameter is used.
394 /// Currently, it must always be 2592000 (30 days) if specified.
395 pub fn with_subscription_period(mut self, value: Integer) -> Self {
396 self.subscription_period = Some(value);
397 self
398 }
399}
400
401impl Method for CreateInvoiceLink {
402 type Response = String;
403
404 fn into_payload(self) -> Payload {
405 Payload::json("createInvoiceLink", self)
406 }
407}
408
409/// Sends an invoice.
410#[serde_with::skip_serializing_none]
411#[derive(Clone, Debug, Serialize)]
412pub struct SendInvoice {
413 chat_id: ChatId,
414 currency: String,
415 description: String,
416 payload: String,
417 prices: Vec<LabeledPrice>,
418 title: String,
419 allow_paid_broadcast: Option<bool>,
420 disable_notification: Option<bool>,
421 message_effect_id: Option<String>,
422 message_thread_id: Option<Integer>,
423 #[serde(flatten)]
424 parameters: Option<InvoiceParameters>,
425 protect_content: Option<bool>,
426 reply_markup: Option<InlineKeyboardMarkup>,
427 reply_parameters: Option<ReplyParameters>,
428 start_parameter: Option<String>,
429}
430
431impl SendInvoice {
432 /// Creates a new `SendInvoice`.
433 ///
434 /// # Arguments
435 ///
436 /// * `chat_id` - Unique identifier of the target chat.
437 /// * `title` - Product name; 1-32 characters.
438 /// * `description` - Product description; 1-255 characters.
439 /// * `payload` - Bot-defined invoice payload; 1-128 bytes
440 /// this will not be displayed to the user;
441 /// use for your internal processes.
442 /// * `currency` - Three-letter ISO 4217 currency code, see more on currencies.
443 /// * `prices` - Price breakdown, a list of components
444 /// (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.).
445 pub fn new<A, B, C, D, E, F>(chat_id: A, title: B, description: C, payload: D, currency: E, prices: F) -> Self
446 where
447 A: Into<ChatId>,
448 B: Into<String>,
449 C: Into<String>,
450 D: Into<String>,
451 E: Into<String>,
452 F: IntoIterator<Item = LabeledPrice>,
453 {
454 SendInvoice {
455 chat_id: chat_id.into(),
456 title: title.into(),
457 description: description.into(),
458 payload: payload.into(),
459 currency: currency.into(),
460 prices: prices.into_iter().collect(),
461 allow_paid_broadcast: None,
462 disable_notification: None,
463 message_effect_id: None,
464 message_thread_id: None,
465 parameters: None,
466 protect_content: None,
467 reply_markup: None,
468 reply_parameters: None,
469 start_parameter: None,
470 }
471 }
472
473 /// Sets a new value for the `allow_paid_broadcast` flag.
474 ///
475 /// # Arguments
476 ///
477 /// * `value` - Whether to allow up to 1000 messages per second, ignoring broadcasting limits
478 /// for a fee of 0.1 Telegram Stars per message.
479 /// The relevant Stars will be withdrawn from the bot's balance.
480 pub fn with_allow_paid_broadcast(mut self, value: bool) -> Self {
481 self.allow_paid_broadcast = Some(value);
482 self
483 }
484
485 /// Sets a new value for the `disable_notification` flag.
486 ///
487 /// # Arguments
488 ///
489 /// * `value` - Indicates whether to send the message silently or not;
490 /// a user will receive a notification without sound.
491 pub fn with_disable_notification(mut self, value: bool) -> Self {
492 self.disable_notification = Some(value);
493 self
494 }
495
496 /// Sets a new message effect ID.
497 ///
498 /// # Arguments
499 ///
500 /// * `value` - Unique identifier of the message effect to be added to the message; for private chats only.
501 pub fn with_message_effect_id<T>(mut self, value: T) -> Self
502 where
503 T: Into<String>,
504 {
505 self.message_effect_id = Some(value.into());
506 self
507 }
508
509 /// Sets a new message thread ID.
510 ///
511 /// # Arguments
512 ///
513 /// * `value` - Unique identifier of the target message thread;
514 /// supergroups only.
515 pub fn with_message_thread_id(mut self, value: Integer) -> Self {
516 self.message_thread_id = Some(value);
517 self
518 }
519
520 /// Sets a new invoice parameters.
521 ///
522 /// # Arguments
523 ///
524 /// * `value` - Invoice parameters.
525 pub fn with_parameters(mut self, value: InvoiceParameters) -> Self {
526 self.parameters = Some(value);
527 self
528 }
529
530 /// Sets a new value for the `protect_content` flag.
531 ///
532 /// # Arguments
533 ///
534 /// * `value` - Indicates whether to protect the contents
535 /// of the sent message from forwarding and saving.
536 pub fn with_protect_content(mut self, value: bool) -> Self {
537 self.protect_content = Some(value);
538 self
539 }
540
541 /// Sets a new reply markup.
542 ///
543 /// # Arguments
544 ///
545 /// * `value` - Reply markup.
546 ///
547 /// If empty, one 'Pay total price' button will be shown.
548 /// If not empty, the first button must be a Pay button.
549 pub fn with_reply_markup<T>(mut self, value: T) -> Self
550 where
551 T: Into<InlineKeyboardMarkup>,
552 {
553 self.reply_markup = Some(value.into());
554 self
555 }
556
557 /// Sets new reply parameters.
558 ///
559 /// # Arguments
560 ///
561 /// * `value` - Description of the message to reply to.
562 pub fn with_reply_parameters(mut self, value: ReplyParameters) -> Self {
563 self.reply_parameters = Some(value);
564 self
565 }
566
567 /// Sets a new unique deep-linking parameter.
568 ///
569 /// # Arguments
570 ///
571 /// * `value` - Value of the parameter.
572 ///
573 /// If left empty, forwarded copies of the sent message will have a Pay button,
574 /// allowing multiple users to pay directly from the forwarded message, using the same invoice.
575 /// If non-empty, forwarded copies of the sent message will have a URL button
576 /// with a deep link to the bot (instead of a Pay button),
577 /// with the value used as the start parameter.
578 pub fn with_start_parameter<T>(mut self, value: T) -> Self
579 where
580 T: Into<String>,
581 {
582 self.start_parameter = Some(value.into());
583 self
584 }
585}
586
587impl Method for SendInvoice {
588 type Response = Message;
589
590 fn into_payload(self) -> Payload {
591 Payload::json("sendInvoice", self)
592 }
593}