Payment Example Bot
In this example section, we will create a bot that demonstrates Telegram's Stars payment system. The bot will send invoices, process payments, and handle refunds, providing a complete workflow for handling monetary transactions.
We will use two different types of handlers:
- onMessageor- @MessageHandlerfor handling commands (- /start,- /pay) and successful payment updates.
- onPreCheckoutor- @PreCheckoutHandlerfor handling Precheckout queries (authorizing payments to proceed).
Import necessary classes
package io.github.natanimn;
import io.github.natanimn.telebof.BotClient;
import io.github.natanimn.telebof.BotContext;
import io.github.natanimn.telebof.enums.ParseMode;
import io.github.natanimn.telebof.types.payments.LabeledPrice;
import io.github.natanimn.telebof.types.updates.Message;
import io.github.natanimn.telebof.types.updates.PreCheckoutQuery;
package io.github.natanimn;
import io.github.natanimn.telebof.BotClient;
import io.github.natanimn.telebof.BotContext;
import io.github.natanimn.telebof.enums.ParseMode;
import io.github.natanimn.telebof.enums.MessageType;
import io.github.natanimn.telebof.types.payments.LabeledPrice;
import io.github.natanimn.telebof.types.updates.Message;
import io.github.natanimn.telebof.types.updates.PreCheckoutQuery;
import io.github.natanimn.telebof.annotations.MessageHandler;
import io.github.natanimn.telebof.annotations.PreCheckoutHandler;
Let us create the PaymentExampleBot class and initialize the BotClient with a token from an environment variable
Our bot will be handling the following events:
- /startcommand
- /paycommand
- Precheckout queries (required step before payment is finalized)
- Successful payment updates
Create /start handler
When the user types /start, the bot responds with a welcome message explaining its purpose.
@MessageHandler(commands = "start")
void start(BotContext context, Message message){
    var user = message.from;
    context.sendMessage(message.chat.id, String.format(
            "<b>Hello %s!</b>,\n\nI am payment example bot.\n<i>Send /pay to try it out.</i>", user.mention()
    ))
     .parseMode(ParseMode.HTML)
     .exec();
}

Create /pay handler and send an invoice
When the user types /pay, the bot sends an invoice for 10 Telegram Stars.
static void pay(BotContext context, Message message){
    // Create a price object for 10 Telegram Stars
    LabeledPrice price = new LabeledPrice("Pay 10 star", 10);
    /**
     * Send an invoice with the following parameters:
     * - chatId: The chat to send the invoice to
     * - title: The title of the invoice
     * - description: A description of what the user is paying for
     * - payload: A unique identifier for this transaction (used for validation)
     * - currency: "XTR" is the currency code for Telegram Stars
     * - prices: An array of LabeledPrice objects defining the cost
     */
    context.sendInvoice(
            message.chat.id,
            "Payment test",
            "Pay 10 start for telebof payment test bot",
            "pay-10", // In a real application, this should be a unique value per transaction
            "XTR",
            new LabeledPrice[]{price}
    ).exec();
}
@MessageHandler(commands = "pay")
void pay(BotContext context, Message message){
    // Create a price object for 10 Telegram Stars
    LabeledPrice price = new LabeledPrice("Pay 10 star", 10);
    /**
     * Send an invoice with the following parameters:
     * - chatId: The chat to send the invoice to
     * - title: The title of the invoice
     * - description: A description of what the user is paying for
     * - payload: A unique identifier for this transaction (used for validation)
     * - currency: "XTR" is the currency code for Telegram Stars
     * - prices: An array of LabeledPrice objects defining the cost
     */
    context.sendInvoice(
            message.chat.id,
            "Payment test",
            "Pay 10 start for telebof payment test bot",
            "pay-10", // In a real application, this should be a unique value per transaction
            "XTR",
            new LabeledPrice[]{price}
    ).exec();
}

Handle PreCheckout Query
After the user confirms payment in the invoice, Telegram sends a precheckout query to the bot. The bot must answer this query within a short time window to allow the payment to proceed or fail.
This is a crucial step where you would typically:
- Validate the payment details (e.g., check item prices, ensure the payloadis valid).
- Check if the items are still in stock.
- Based on this check, confirm or deny the payment.
For this example, we automatically confirm all queries.
Explanation:
- The PreCheckoutQueryobject contains all details about the impending payment.
- Calling answerPreCheckoutQuerywithtruetells Telegram the payment is authorized to be completed by the user.
- Calling it with falseand an optional error message would decline the payment and inform the user.
Handle Successful Payment
Once the Precheckout query is answered successfully and the user completes the payment, Telegram sends an Update containing a Message with a successful_payment field.
In a real application, this is where you would:
- Grant the user access to the purchased product or service.
- Generate and send a license key.
- Update your database to record the transaction.
- Fulfill the order.
For this demonstration, we immediately refund the payment.
static void acceptPayment(BotContext context, Message message){
    // Extract the payment details from the message
    var payment = message.successful_payment;
    // REFUND THE PAYMENT (DEMONSTRATION ONLY)
    // This showcases the refundStars method.
    // In a real application, you would NOT do this; you would deliver the product instead.
    context.refundStarPayment(message.from.id, payment.telegram_payment_charge_id).exec();
    // Inform the user that their payment was received and immediately refunded
    context.sendMessage(message.chat.id,
            "<b>Yay, your money is refunded.</b>\n\n" +
            "<i>Thanks for your generous donation</i>"
    ).parseMode(ParseMode.HTML).exec();
}
@MessageHandler(type = MessageType.SUCCESSFUL_PAYMENT)
void acceptPayment(BotContext context, Message message){
    // Extract the payment details from the message
    var payment = message.successful_payment;
    // REFUND THE PAYMENT (DEMONSTRATION ONLY)
    // This showcases the refundStars method.
    // In a real application, you would NOT do this; you would deliver the product instead.
    context.refundStarPayment(message.from.id, payment.telegram_payment_charge_id).exec();
    // Inform the user that their payment was received and immediately refunded
    context.sendMessage(message.chat.id,
            "<b>Yay, your money is refunded.</b>\n\n" +
            "<i>Thanks for your generous donation</i>"
    ).parseMode(ParseMode.HTML).exec();
}
Important Note: The
refundStarPaymentcall is for demonstration purposes only. A real bot would not charge users and then instantly refund them. This method is useful for handling legitimate refund requests or errors.
Final Setup and Execution
All handlers are registered in the main method, and the bot is started using Long Polling.
public static void main(String[] args){
// Initialize the bot client with the token from an environment variable
    final var bot = new BotClient(TOKEN);
    // Register command handlers
    bot.onMessage(filter -> filter.commands("start"), PaymentExampleBot::start);
    bot.onMessage(filter -> filter.commands("pay"), PaymentExampleBot::pay);
    // Register payment handlers
    bot.onPreCheckout(filter -> true, PaymentExampleBot::processPrecheckOut);
    bot.onMessage(filter -> filter.successfulPayment(), PaymentExampleBot::acceptPayment);
    // Start the bot
    bot.startPolling();
}
public class PaymentExampleBot {
    static String TOKEN = System.getenv("TOKEN");
    public static void main(String[] args){
        BotClient bot = new BotClient(TOKEN);
        bot.addHandler(new PaymentExampleBot());
        bot.startPolling();
    }
    @MessageHandler(commands = "start")
    void start(BotContext context, Message message){ /* ... */ }
    @MessageHandler(commands = "pay")
    void pay(BotContext context, Message message){ /* ... */ }
    @PreCheckoutHandler
    void processPrecheckOut(BotContext context, PreCheckoutQuery query){ /* ... */ }
    @MessageHandler(type = MessageType.SUCCESSFUL_PAYMENT)
    void acceptPayment(BotContext context, Message message){ /* ... */ }
}

The full source code can be found on examples/payment_bot