如何使用 TeleBot 的 send_invoice method 发送发票?
结论
TeleBot.send_invoice() 的 prices 参数需要传入由 telebot.types.LabeledPrice 对象组成的列表,不能直接使用字典列表。字典里即使包含 label 和 amount,部分版本的 pyTelegramBotAPI 也可能无法将其正确序列化为 Telegram Bot API 所需的价格数组。Telegram 最终收到的价格列表为空时,会返回以下错误:
Bad Request: there must be at least one price
正确写法
from telebot import types
prices = [
types.LabeledPrice(label="flowers", amount=1000)
]
bot.send_invoice(
chat_id=chat_id,
title="Flowers Delivery",
description=description_of_package,
invoice_payload="some-invoice-payload-for-internal-use",
provider_token=keys.TEST_TRANZZO_PAYMENT,
currency="UAH",
prices=prices,
start_parameter="test-start-parameter"
)
也可以在调用方法时直接创建价格对象:
from telebot import types
bot.send_invoice(
chat_id=chat_id,
title="Flowers Delivery",
description=description_of_package,
invoice_payload="some-invoice-payload-for-internal-use",
provider_token=keys.TEST_TRANZZO_PAYMENT,
currency="UAH",
prices=[
types.LabeledPrice(label="flowers", amount=1000)
],
start_parameter="test-start-parameter"
)
原因
Telegram Bot API 要求 prices 是一个非空的 LabeledPrice 数组,其中每个价格项都必须包含:
label:价格项目名称。amount:以货币最小单位表示的整数金额。
发送请求前,pyTelegramBotAPI 会把 Python 对象转换成 Telegram API 所需的 JSON。使用 types.LabeledPrice,可以让价格项按照库预期的格式完成序列化。
如果直接传入字典:
prices=[{"label": "flowers", "amount": 1000}]
部分版本可能无法正确处理。这样一来,代码中虽然写了价格,Telegram 实际收到的 prices 却可能仍是空列表。
金额单位
amount 必须是以货币最小单位表示的整数,不能使用浮点数。对于保留两位小数的货币:
types.LabeledPrice(label="flowers", amount=1000)
通常表示 10.00 UAH,不是 1000 UAH。如果要收取 1000.00 UAH,通常应写为:
types.LabeledPrice(label="flowers", amount=100000)
实际换算方式应以 Telegram 对该货币采用的小数位数为准。
还需要处理预结账请求
发票成功发送后,机器人还要在用户付款前处理 pre_checkout_query,并及时确认:
@bot.pre_checkout_query_handler(func=lambda query: True)
def process_pre_checkout_query(query):
bot.answer_pre_checkout_query(
pre_checkout_query_id=query.id,
ok=True
)
在实际业务中,应先检查 query.invoice_payload、商品状态和金额等信息,确认无误后再返回 ok=True。如果校验失败,则应返回 ok=False 并提供错误说明。
机器人还需要处理包含 successful_payment 的消息,以便在支付成功后执行发货、更新订单等操作。支付服务商 token、币种和金额也必须符合对应支付服务商的配置要求。
备注:内容仅供参考。
