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}