1#!/usr/bin/env python
2# pylint: disable=unused-argument, wrong-import-position
3# This program is dedicated to the public domain under the CC0 license.
4
5"""Basic example for a bot that can receive payment from user."""
6
7import logging
8
9from telegram import __version__ as TG_VER
10
11try:
12 from telegram import __version_info__
13except ImportError:
14 __version_info__ = (0, 0, 0, 0, 0) # type: ignore[assignment]
15
16if __version_info__ < (20, 0, 0, "alpha", 1):
17 raise RuntimeError(
18 f"This example is not compatible with your current PTB version {TG_VER}. To view the "
19 f"{TG_VER} version of this example, "
20 f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html"
21 )
22from telegram import LabeledPrice, ShippingOption, Update
23from telegram.ext import (
24 Application,
25 CommandHandler,
26 ContextTypes,
27 MessageHandler,
28 PreCheckoutQueryHandler,
29 ShippingQueryHandler,
30 filters,
31)
32
33# Enable logging
34logging.basicConfig(
35 format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
36)
37# set higher logging level for httpx to avoid all GET and POST requests being logged
38logging.getLogger("httpx").setLevel(logging.WARNING)
39
40logger = logging.getLogger(__name__)
41
42PAYMENT_PROVIDER_TOKEN = "PAYMENT_PROVIDER_TOKEN"
43
44
45async def start_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
46 """Displays info on how to use the bot."""
47 msg = (
48 "Use /shipping to get an invoice for shipping-payment, or /noshipping for an "
49 "invoice without shipping."
50 )
51
52 await update.message.reply_text(msg)
53
54
55async def start_with_shipping_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
56 """Sends an invoice with shipping-payment."""
57 chat_id = update.message.chat_id
58 title = "Payment Example"
59 description = "Payment Example using python-telegram-bot"
60 # select a payload just for you to recognize its the donation from your bot
61 payload = "Custom-Payload"
62 # In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
63 currency = "USD"
64 # price in dollars
65 price = 1
66 # price * 100 so as to include 2 decimal points
67 # check https://core.telegram.org/bots/payments#supported-currencies for more details
68 prices = [LabeledPrice("Test", price * 100)]
69
70 # optionally pass need_name=True, need_phone_number=True,
71 # need_email=True, need_shipping_address=True, is_flexible=True
72 await context.bot.send_invoice(
73 chat_id,
74 title,
75 description,
76 payload,
77 PAYMENT_PROVIDER_TOKEN,
78 currency,
79 prices,
80 need_name=True,
81 need_phone_number=True,
82 need_email=True,
83 need_shipping_address=True,
84 is_flexible=True,
85 )
86
87
88async def start_without_shipping_callback(
89 update: Update, context: ContextTypes.DEFAULT_TYPE
90) -> None:
91 """Sends an invoice without shipping-payment."""
92 chat_id = update.message.chat_id
93 title = "Payment Example"
94 description = "Payment Example using python-telegram-bot"
95 # select a payload just for you to recognize its the donation from your bot
96 payload = "Custom-Payload"
97 # In order to get a provider_token see https://core.telegram.org/bots/payments#getting-a-token
98 currency = "USD"
99 # price in dollars
100 price = 1
101 # price * 100 so as to include 2 decimal points
102 prices = [LabeledPrice("Test", price * 100)]
103
104 # optionally pass need_name=True, need_phone_number=True,
105 # need_email=True, need_shipping_address=True, is_flexible=True
106 await context.bot.send_invoice(
107 chat_id, title, description, payload, PAYMENT_PROVIDER_TOKEN, currency, prices
108 )
109
110
111async def shipping_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
112 """Answers the ShippingQuery with ShippingOptions"""
113 query = update.shipping_query
114 # check the payload, is this from your bot?
115 if query.invoice_payload != "Custom-Payload":
116 # answer False pre_checkout_query
117 await query.answer(ok=False, error_message="Something went wrong...")
118 return
119
120 # First option has a single LabeledPrice
121 options = [ShippingOption("1", "Shipping Option A", [LabeledPrice("A", 100)])]
122 # second option has an array of LabeledPrice objects
123 price_list = [LabeledPrice("B1", 150), LabeledPrice("B2", 200)]
124 options.append(ShippingOption("2", "Shipping Option B", price_list))
125 await query.answer(ok=True, shipping_options=options)
126
127
128# after (optional) shipping, it's the pre-checkout
129async def precheckout_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
130 """Answers the PreQecheckoutQuery"""
131 query = update.pre_checkout_query
132 # check the payload, is this from your bot?
133 if query.invoice_payload != "Custom-Payload":
134 # answer False pre_checkout_query
135 await query.answer(ok=False, error_message="Something went wrong...")
136 else:
137 await query.answer(ok=True)
138
139
140# finally, after contacting the payment provider...
141async def successful_payment_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
142 """Confirms the successful payment."""
143 # do something after successfully receiving payment?
144 await update.message.reply_text("Thank you for your payment!")
145
146
147def main() -> None:
148 """Run the bot."""
149 # Create the Application and pass it your bot's token.
150 application = Application.builder().token("TOKEN").build()
151
152 # simple start function
153 application.add_handler(CommandHandler("start", start_callback))
154
155 # Add command handler to start the payment invoice
156 application.add_handler(CommandHandler("shipping", start_with_shipping_callback))
157 application.add_handler(CommandHandler("noshipping", start_without_shipping_callback))
158
159 # Optional handler if your product requires shipping
160 application.add_handler(ShippingQueryHandler(shipping_callback))
161
162 # Pre-checkout handler to final check
163 application.add_handler(PreCheckoutQueryHandler(precheckout_callback))
164
165 # Success! Notify your user!
166 application.add_handler(
167 MessageHandler(filters.SUCCESSFUL_PAYMENT, successful_payment_callback)
168 )
169
170 # Run the bot until the user presses Ctrl-C
171 application.run_polling(allowed_updates=Update.ALL_TYPES)
172
173
174if __name__ == "__main__":
175 main()