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