Skip to content
πŸ’³ Payments Guide

Telegram Bot Payments Integration: Step-by-Step Guide with Code

A client needed payments by morning. By 6 AM, their bot was processing Stripe transactions. Here is exactly how.

Telegram Bot Payments Integration: Step-by-Step Guide with Code
⏱ 18 min readπŸ“ ~6000 wordsπŸ“… August 29, 2026πŸ‘€ Dmitry Malyshev

Payment Options for Telegram Bots: What Works in 2026

A client messaged me at 2 AM: "My bot takes orders but I still invoice manually β€” can you fix this by morning?" By 6 AM, their bot was processing payments through Stripe. Here is exactly how I did it β€” and how you can do the same in 30 minutes.

Before diving into code, let me show you every payment option available for Telegram bots β€” because the right choice depends on your business, not on what is easiest to implement.

← Swipe β†’
Payment MethodSetup TimeTransaction FeesBest ForComplexity
Telegram Payments (native)15 min0% (Telegram) + provider feesSimple stores, quick setupLow
Stripe30 min2.9% + $0.30/txnUS businesses, cards, Apple PayMedium
PayPal30 min3.49% + $0.49/txnPayPal users, internationalMedium
Crypto (NOWPayments)20 min0.5% – 1%Crypto-native audiencesLow–Medium
Manual invoice0 min0% (your time is the cost)Testing onlyNone

Telegram Payments (Native)

Telegram has a built-in payment system that works through third-party providers. The customer never leaves Telegram β€” the payment form opens as a native overlay.

Supported providers: Stripe, PayPal, YooMoney, Sberbank, and 10+ others.

Pros: - Zero friction β€” customer stays in Telegram - Native UI that feels like part of the app - No web view redirect - Telegram does not charge any fee (only the payment provider does)

Cons: - Limited customization of the payment form - Not all providers available in all countries - No subscription/recurring payments natively

Stripe

The most popular choice for US-based businesses. Stripe supports credit/debit cards, Apple Pay, Google Pay, ACH, and 135+ currencies.

Why Stripe wins for most bots: - Best documentation in the industry - Webhook-based architecture (perfect for bots) - Stripe Checkout handles PCI compliance for you - Supports refunds, subscriptions, and invoicing

PayPal

Essential if your customers prefer PayPal β€” especially in the US where 400+ million accounts are active. PayPal adds an extra login step, but for PayPal loyalists, it is a dealbreaker if missing.

Crypto (NOWPayments)

A niche but growing option. NOWPayments integrates easily and supports 100+ cryptocurrencies. Best for tech-savvy audiences or international customers who prefer crypto.

Which Should You Choose?

← Swipe β†’
Your SituationRecommendedWhy
US-based, selling to US customersStripeLowest friction, best ecosystem
International customersStripe + PayPalCover both card and PayPal users
Quick MVP, minimal setupTelegram Payments + StripeNative feel, fast to implement
Crypto-native audienceStripe + NOWPaymentsTraditional + crypto options
Subscription productsStripeOnly Stripe handles recurring well

My recommendation for 90% of projects: start with Stripe via Telegram Payments. It gives you the native Telegram experience with Stripe's powerful backend. Add PayPal later if customer data shows demand.

πŸ’¬ Not sure which payment method fits your business? I have integrated all of them. Tell me about your store and I will recommend the best setup β€” bots from $500, delivered in 5-7 days β†’

Step-by-Step: Adding Stripe Payments to Your Telegram Bot

This is the exact code I use in production for Stripe integration. It takes about 30 minutes to set up from scratch.

Prerequisites

Before you start, you need: 1. A Stripe account (free at stripe.com) 2. Your Stripe API keys (test + production) 3. A Telegram bot token (from @BotFather) 4. Python 3.10+ with aiogram 3.x installed 5. A server with a public URL (for webhooks)

Step 1: Install Dependencies

Bash
 1# Install required libraries
 2pip install aiogram stripe aiohttp

Step 2: Configure Stripe

Python
 1# stripe_config.py β€” Stripe configuration
 2# Store keys in environment variables, NOT in code!
 3
 4import os
 5import stripe
 6
 7# Test keys (for development)
 8# Find them in Stripe Dashboard β†’ Developers β†’ API keys
 9stripe.api_key = os.getenv("STRIPE_SECRET_KEY")  # sk_test_...
10STRIPE_WEBHOOK_SECRET = os.getenv("STRIPE_WEBHOOK_SECRET")  # whsec_...
11
12# Public URL of your server (needed for webhook)
13# In production replace with your domain
14WEBHOOK_URL = os.getenv("WEBHOOK_URL", "https://your-server.com/webhook/stripe")

Step 3: Create a Payment Session

When the customer taps "Pay," the bot creates a Stripe Checkout Session and sends them a payment link.

Python
 1# payments.py β€” creating Stripe payment session
 2
 3import stripe
 4from aiogram import Router, F
 5from aiogram.types import CallbackQuery, InlineKeyboardButton
 6from aiogram.utils.keyboard import InlineKeyboardBuilder
 7
 8router = Router()
 9
10async def create_checkout_session(
11    user_id: int,
12    order_id: str,
13    items: list[dict],
14    bot_username: str,
15) -> str:
16    """
17    Creates Stripe Checkout Session for order payment.
18    Returns URL to redirect user to payment.
19
20    Parameters:
21    - user_id: Telegram ID of the buyer
22    - order_id: order number in our system
23    - items: list of items [{name, price, quantity}]
24    - bot_username: bot name (for return after payment)
25    """
26    # Build product list for Stripe
27    line_items = []
28    for item in items:
29        line_items.append({
30            "price_data": {
31                "currency": "usd",  # Payment currency
32                "product_data": {
33                    "name": item["name"],  # Product name
34                },
35                # Stripe accepts price in cents (100 = $1.00)
36                "unit_amount": int(item["price"] * 100),
37            },
38            "quantity": item["quantity"],  # Quantity
39        })
40
41    # Create payment session
42    session = stripe.checkout.Session.create(
43        payment_method_types=["card"],  # Accept cards
44        line_items=line_items,
45        mode="payment",  # One-time payment (not subscription)
46        # Return URL after successful payment
47        success_url=f"https://t.me/{bot_username}?start=payment_success_{order_id}",
48        # Return URL on payment cancellation
49        cancel_url=f"https://t.me/{bot_username}?start=payment_cancel_{order_id}",
50        # Metadata β€” link payment to our order
51        metadata={
52            "telegram_user_id": str(user_id),
53            "order_id": order_id,
54        },
55    )
56
57    return session.url  # Payment URL
58
59
60@router.callback_query(F.data.startswith("pay:"))
61async def handle_payment(callback: CallbackQuery):
62    """
63    Handler for "Pay" button.
64    Callback data format: "pay:order_id"
65    """
66    order_id = callback.data.split(":")[1]
67
68    # TODO: get order items from database
69    # items = await db.get_order_items(order_id)
70    # Using test data for now:
71    items = [
72        {"name": "T-Shirt (Black, M)", "price": 29.99, "quantity": 1},
73        {"name": "Shipping", "price": 5.99, "quantity": 1},
74    ]
75
76    # Create payment session
77    payment_url = await create_checkout_session(
78        user_id=callback.from_user.id,
79        order_id=order_id,
80        items=items,
81        bot_username=callback.bot.username,
82    )
83
84    # Send button with payment link
85    builder = InlineKeyboardBuilder()
86    builder.row(InlineKeyboardButton(
87        text="πŸ’³ Pay with Card",
88        url=payment_url,  # Stripe Checkout link
89    ))
90
91    await callback.message.edit_text(
92        text=f"πŸ›’ <b>Order #{order_id}</b>\n\n"
93             f"Total: $35.98\n\n"
94             f"Tap the button below to pay securely via Stripe:",
95        reply_markup=builder.as_markup(),
96    )
97    await callback.answer()

Step 4: Handle Webhooks (Payment Confirmation)

Stripe sends a webhook when payment succeeds. Your bot listens for it and confirms the order.

Python
 1# webhook.py β€” handling webhook from Stripe
 2
 3from aiohttp import web
 4import stripe
 5import json
 6
 7# Webhook secret key (from Stripe Dashboard β†’ Webhooks)
 8WEBHOOK_SECRET = "whsec_your_webhook_secret_here"
 9
10async def stripe_webhook_handler(request: web.Request) -> web.Response:
11    """
12    Receives webhook events from Stripe.
13    Stripe sends confirmation here after each payment.
14    """
15    # Get request body and signature
16    payload = await request.body()
17    sig_header = request.headers.get("Stripe-Signature")
18
19    try:
20        # Verify signature β€” ensure request is from Stripe
21        event = stripe.Webhook.construct_event(
22            payload, sig_header, WEBHOOK_SECRET
23        )
24    except ValueError:
25        # Invalid payload
26        return web.Response(status=400)
27    except stripe.error.SignatureVerificationError:
28        # Invalid signature β€” possibly fake request
29        return web.Response(status=400)
30
31    # Handle successful payment event
32    if event["type"] == "checkout.session.completed":
33        session = event["data"]["object"]
34
35        # Extract metadata from session
36        user_id = int(session["metadata"]["telegram_user_id"])
37        order_id = session["metadata"]["order_id"]
38        amount = session["amount_total"] / 100  # Convert from cents
39
40        # Update order status in database
41        # await db.update_order_status(order_id, "paid")
42
43        # Send confirmation to buyer via Telegram
44        # await bot.send_message(
45        #     chat_id=user_id,
46        #     text=f"βœ… Payment received! Order #{order_id}\n"
47        #          f"Amount: ${amount:.2f}\n"
48        #          f"We will ship your order soon!"
49        # )
50
51        print(f"βœ… Payment confirmed: order={order_id}, user={user_id}, amount=${amount}")
52
53    return web.Response(status=200)
54
55
56def setup_webhook_app(bot) -> web.Application:
57    """
58    Configures aiohttp application for receiving webhook.
59    Runs on the same server as the bot.
60    """
61    app = web.Application()
62    # Register webhook handler at /webhook/stripe
63    app.router.add_post("/webhook/stripe", stripe_webhook_handler)
64    return app

Step 5: Test with Stripe Test Mode

Stripe provides test card numbers β€” no real money involved:

← Swipe β†’
Card NumberScenario
4242 4242 4242 4242Successful payment
4000 0000 0000 0002Declined payment
4000 0025 0000 3155Requires 3D Secure

Use any future expiry date (12/34) and any 3-digit CVC.

Your first action: create a free Stripe account at stripe.com and grab your test API keys. That takes 5 minutes and unblocks everything else.

πŸ’¬ Need help setting up Stripe for your bot? I have integrated Stripe into 20+ Telegram bots. Get a working payment bot in 3 days β€” from $500 β†’

Adding PayPal to Your Telegram Bot

PayPal is the second most requested payment method after Stripe. Here is how to add it to your Telegram bot.

Why Add PayPal?

← Swipe β†’
FactorStripePayPal
User baseCard holders (everyone)400M+ PayPal accounts
Extra login stepNoYes (PayPal login)
Transaction fees2.9% + $0.303.49% + $0.49
Refund handlingAPI-basedAPI-based
Subscription supportExcellentGood
International135+ currencies200+ markets
Setup complexityMediumMedium

Step 1: Set Up PayPal Developer Account

  1. 1.Go to developer.paypal.com
  2. 2.Create a REST API app
  3. 3.Get your Client ID and Secret
  4. 4.Switch between Sandbox (testing) and Live (production)

Step 2: Create a PayPal Order

Python
 1# paypal_payments.py β€” creating order via PayPal API
 2
 3import aiohttp
 4import os
 5
 6# PayPal keys (from Developer Dashboard)
 7PAYPAL_CLIENT_ID = os.getenv("PAYPAL_CLIENT_ID")
 8PAYPAL_SECRET = os.getenv("PAYPAL_SECRET")
 9
10# Base URL (sandbox for testing, api-m for production)
11PAYPAL_BASE = "https://api-m.sandbox.paypal.com"  # Test mode
12# PAYPAL_BASE = "https://api-m.paypal.com"  # Production
13
14async def get_paypal_access_token() -> str:
15    """
16    Gets PayPal API access token.
17    Token lives 8 hours, needs refreshing.
18    """
19    async with aiohttp.ClientSession() as session:
20        # Authorization via Basic Auth
21        auth = aiohttp.BasicAuth(PAYPAL_CLIENT_ID, PAYPAL_SECRET)
22        async with session.post(
23            f"{PAYPAL_BASE}/v1/oauth2/token",
24            auth=auth,
25            data={"grant_type": "client_credentials"},
26        ) as resp:
27            data = await resp.json()
28            return data["access_token"]
29
30
31async def create_paypal_order(
32    order_id: str,
33    items: list[dict],
34    total_amount: float,
35) -> dict:
36    """
37    Creates order in PayPal and returns payment link.
38
39    Parameters:
40    - order_id: order number in our system
41    - items: list of items [{name, price, quantity}]
42    - total_amount: total order amount in dollars
43    """
44    token = await get_paypal_access_token()
45
46    # Build request body for PayPal
47    order_body = {
48        "intent": "CAPTURE",  # Capture payment immediately
49        "purchase_units": [{
50            "reference_id": order_id,  # Our order number
51            "amount": {
52                "currency_code": "USD",
53                # Split amount into items and shipping
54                "value": f"{total_amount:.2f}",
55                "breakdown": {
56                    "item_total": {
57                        "currency_code": "USD",
58                        "value": f"{total_amount:.2f}",
59                    }
60                }
61            },
62            # List of items in order
63            "items": [
64                {
65                    "name": item["name"],
66                    "unit_amount": {
67                        "currency_code": "USD",
68                        "value": f"{item['price']:.2f}",
69                    },
70                    "quantity": str(item["quantity"]),
71                }
72                for item in items
73            ],
74        }],
75        # Where to return user after payment
76        "application_context": {
77            "return_url": f"https://your-server.com/paypal/success?order={order_id}",
78            "cancel_url": f"https://your-server.com/paypal/cancel?order={order_id}",
79        }
80    }
81
82    async with aiohttp.ClientSession() as session:
83        async with session.post(
84            f"{PAYPAL_BASE}/v2/checkout/orders",
85            headers={
86                "Authorization": f"Bearer {token}",
87                "Content-Type": "application/json",
88            },
89            json=order_body,
90        ) as resp:
91            data = await resp.json()
92
93            # Look for payment link in response
94            for link in data.get("links", []):
95                if link["rel"] == "approve":
96                    return {
97                        "order_id": data["id"],  # Order ID in PayPal
98                        "approval_url": link["href"],  # Payment link
99                    }
100
101            raise Exception(f"PayPal error: {data}")

Step 3: Capture the Payment (After Customer Approves)

Python
 1# paypal_capture.py β€” capturing payment after buyer approval
 2
 3async def capture_paypal_order(paypal_order_id: str) -> dict:
 4    """
 5    Captures payment after buyer approved it on PayPal page.
 6    Called from webhook or callback.
 7    """
 8    token = await get_paypal_access_token()
 9
10    async with aiohttp.ClientSession() as session:
11        async with session.post(
12            f"{PAYPAL_BASE}/v2/checkout/orders/{paypal_order_id}/capture",
13            headers={
14                "Authorization": f"Bearer {token}",
15                "Content-Type": "application/json",
16            },
17        ) as resp:
18            data = await resp.json()
19
20            # Check capture status
21            if data["status"] == "COMPLETED":
22                # Payment successful!
23                payer_email = data["payer"]["email_address"]
24                amount = data["purchase_units"][0]["payments"]["captures"][0]["amount"]["value"]
25
26                return {
27                    "status": "completed",
28                    "payer_email": payer_email,
29                    "amount": float(amount),
30                }
31            else:
32                return {"status": data["status"], "error": "Payment not completed"}

Stripe vs PayPal: My Recommendation

For most Telegram bots, I recommend Stripe as primary and PayPal as secondary. Here is why:

  • Stripe has lower fees (2.9% vs 3.49%)
  • Stripe Checkout works better inside Telegram's web view
  • Stripe's webhook system is more reliable for bot integrations
  • PayPal adds a login step that increases friction

Add PayPal only if: - Your analytics show 20%+ of customers attempt PayPal - You sell internationally where PayPal is dominant - Your product price is over $100 (PayPal's buyer protection is a trust signal)

I wrote a comprehensive comparison of all payment options in my Telegram bot payments integration overview section above. For e-commerce specific guidance, see my Telegram bot for e-commerce guide.

πŸ’¬ Want both Stripe and PayPal in your bot? I build payment integrations that handle both seamlessly β€” the customer chooses their preferred method at checkout. Get a quote β€” payment bots from $800 β†’

Telegram Native Payments: The Fastest Way to Accept Money

Telegram has its own payment system built into the Bot API. It is the fastest way to add payments β€” about 15 minutes of code β€” and the customer never leaves Telegram.

How Telegram Payments Work

  1. 1.Bot sends an invoice (a special message with product details and a "Pay" button)
  2. 2.Customer taps "Pay" β†’ Telegram shows a native payment form
  3. 3.Customer enters card details (or selects a saved card)
  4. 4.Payment is processed by the provider (Stripe, PayPal, etc.)
  5. 5.Bot receives a successful\_payment event

The key difference from Stripe Checkout: the customer never leaves Telegram. The payment form is a native overlay, not a web page redirect.

Code: Sending an Invoice

Python
 1# telegram_payments.py β€” sending invoice via Telegram Payments API
 2
 3from aiogram import Router, F
 4from aiogram.types import (
 5    LabeledPrice,       # For specifying price
 6    InlineKeyboardButton,
 7)
 8from aiogram.utils.keyboard import InlineKeyboardBuilder
 9
10router = Router()
11
12# Provider token β€” obtained from @BotFather
13# For Stripe: token from Stripe Dashboard β†’ Settings β†’ Payment methods
14PROVIDER_TOKEN = "YOUR_PROVIDER_TOKEN_FROM_BOTFATHER"
15
16@router.callback_query(F.data.startswith("invoice:"))
17async def send_invoice(callback):
18    """
19    Sends invoice to buyer via Telegram Payments.
20    Callback data format: "invoice:order_id"
21    """
22    order_id = callback.data.split(":")[1]
23
24    # Build price list (items + shipping)
25    prices = [
26        LabeledPrice(label="T-Shirt (Black, M)", amount=2999),  # $29.99 in cents
27        LabeledPrice(label="Shipping", amount=599),              # $5.99 in cents
28    ]
29
30    # Send invoice β€” Telegram will show native payment form
31    await callback.bot.send_invoice(
32        chat_id=callback.from_user.chat.id,
33
34        # Main product information
35        title="Order #1234",                    # Invoice title
36        description="T-Shirt (Black, M) + Shipping",  # Description
37        payload=f"order_{order_id}",            # Internal identifier
38        provider_token=PROVIDER_TOKEN,          # Payment provider token
39        currency="usd",                         # Currency
40        prices=prices,                          # List of items
41
42        # Additional parameters (optional)
43        need_name=False,          # Request name?
44        need_phone_number=False,  # Request phone?
45        need_email=False,         # Request email?
46        need_shipping_address=False,  # Request shipping address?
47        is_flexible=False,        # Shipping price depends on address?
48        start_parameter=f"pay_{order_id}",  # Deep link parameter
49    )
50
51    await callback.answer("Invoice sent! Check your messages.")
52
53
54@router.pre_checkout_query()
55async def process_pre_checkout(pre_checkout_query):
56    """
57    Pre-checkout handler β€” Telegram asks "all ok?"
58    before charging.
59    Here you can verify stock, amount, etc.
60    """
61    # Check that item is in stock
62    # If all ok β€” confirm
63    await pre_checkout_query.answer(ok=True)
64
65    # If something is wrong β€” reject with error:
66    # await pre_checkout_query.answer(
67    #     ok=False,
68    #     error_message="Sorry, this item is out of stock."
69    # )
70
71
72@router.message(F.successful_payment)
73async def process_successful_payment(message):
74    """
75    Successful payment handler.
76    Called AFTER money is charged.
77    """
78    payment = message.successful_payment
79
80    # Extract payment data
81    order_payload = payment.invoice_payload    # Our payload ("order_1234")
82    total_amount = payment.total_amount / 100  # Amount in dollars
83    currency = payment.currency                # Currency
84    telegram_charge_id = payment.telegram_payment_id  # Transaction ID in Telegram
85    provider_charge_id = payment.provider_payment_id  # Transaction ID at provider
86
87    # Update order status in database
88    # await db.update_order_status(order_payload, "paid")
89
90    # Send confirmation to buyer
91    await message.answer(
92        f"βœ… <b>Payment successful!</b>\n\n"
93        f"Order: {order_payload}\n"
94        f"Amount: ${total_amount:.2f} {currency}\n\n"
95        f"We will process your order right away! πŸš€"
96    )

Telegram Payments vs Stripe Checkout

← Swipe β†’
FeatureTelegram PaymentsStripe Checkout
Customer stays in Telegramβœ… Yes❌ Redirects to web
Setup time15 min30 min
Saved cardsβœ… Telegram remembersβœ… Stripe remembers
CustomizationLimitedFull control
RefundsVia Stripe APIVia Stripe API
Subscriptions❌ Noβœ… Yes
AnalyticsBasicFull Stripe Dashboard

My recommendation: use Telegram Payments for simple, one-time purchases. Use Stripe Checkout for subscriptions, complex products, or when you need full control over the payment experience.

πŸ’¬ Want the fastest payment setup possible? Telegram Payments with Stripe as the provider gives you the best of both worlds β€” native Telegram UX with Stripe's backend. I can set it up in a day β€” from $500 β†’

Security Best Practices for Payment Bots

Processing payments in a Telegram bot means handling real money. Security is not optional β€” it is the foundation. Here is the checklist I follow on every payment bot project.

← Swipe β†’
#Security MeasurePriorityWhat It Prevents
1Never store card detailsCriticalPCI compliance, data breaches
2Verify webhook signaturesCriticalFake payment confirmations
3Use HTTPS everywhereCriticalMan-in-the-middle attacks
4Validate amounts server-sideHighPrice manipulation
5Rate limit payment endpointsHighAbuse, DoS attacks
6Log all transactionsHighAudit trail, dispute resolution
7Use environment variables for keysHighAccidental key exposure
8Implement idempotencyMediumDouble charges
9Set up fraud alertsMediumSuspicious transactions
10Regular security auditsMediumEmerging vulnerabilities

Rule #1: Never Store Card Details

This is non-negotiable. Let Stripe, PayPal, or Telegram handle card data. Your bot should only store: - Payment provider transaction ID - Order amount and currency - Payment status (pending, completed, refunded) - Timestamp

Never store: card numbers, CVV, expiry dates, or full cardholder names. This is PCI DSS compliance 101.

Rule #2: Verify Webhook Signatures

Every payment provider signs their webhooks. Always verify the signature before processing:

Python
 1# Secure webhook verification from Stripe
 2# ALWAYS verify signature before processing payment
 3
 4import stripe
 5
 6def verify_stripe_webhook(payload: bytes, sig_header: str, secret: str) -> dict:
 7    """
 8    Verifies webhook signature from Stripe.
 9    If signature is invalid β€” raises exception.
10    Never process webhook without signature verification!
11    """
12    try:
13        event = stripe.Webhook.construct_event(
14            payload, sig_header, secret
15        )
16        return event
17    except stripe.error.SignatureVerificationError:
18        # Signature mismatch β€” possible forgery
19        raise ValueError("Invalid webhook signature β€” possible forgery!")

Rule #4: Validate Amounts Server-Side

Never trust the amount sent from the client (the Telegram message). Always recalculate the total on your server:

Python
 1# Server-side order amount validation
 2# Never trust amount from client β€” recalculate yourself
 3
 4async def validate_order_amount(order_id: str, claimed_total: float) -> bool:
 5    """
 6    Recalculates order amount on server and compares
 7    with declared. Protection against price tampering.
 8    """
 9    # Get order items from DB
10    items = await db.get_order_items(order_id)
11
12    # Recalculate amount using DB prices (NOT from client!)
13    calculated_total = sum(
14        item.price * item.quantity  # Price from our DB
15        for item in items
16    )
17
18    # Compare with declared amount
19    # Allow 1 cent tolerance (rounding)
20    return abs(calculated_total - claimed_total) < 0.01

Rule #8: Implement Idempotency

If a webhook is delivered twice (it happens!), your bot should not charge the customer twice. Use the payment provider's transaction ID as a unique key:

Python
 1# Idempotency β€” protection against double charging
 2# If webhook arrived twice β€” ignore second time
 3
 4async def handle_payment_webhook(transaction_id: str, order_id: str, amount: float):
 5    """
 6    Processes payment webhook with duplicate protection.
 7    """
 8    # Check β€” have we already processed this transaction?
 9    existing = await db.get_transaction(transaction_id)
10    if existing:
11        # Already processed β€” ignore (don't charge again!)
12        print(f"⚠️ Duplicate webhook ignored: {transaction_id}")
13        return
14
15    # First time β€” process payment
16    await db.save_transaction(transaction_id, order_id, amount)
17    await db.update_order_status(order_id, "paid")
18    print(f"βœ… Payment processed: {transaction_id}")

Review your bot against this checklist. If you are missing even one critical item (#1–#3), fix it before going live. A single security incident can destroy customer trust permanently.

Payment Bot Testing Checklist: 15 Things to Test Before Launch

I have launched 20+ payment bots. Every single one had at least one bug caught during testing that would have cost real money in production. Here is the exact checklist I use.

Happy Path (Successful Payments)

← Swipe β†’
#Test CaseHow to TestExpected Result
1Successful card paymentUse test card 4242...Order confirmed, receipt sent
2Payment with correct amountCompare bot total vs Stripe totalAmounts match exactly
3Webhook receivedCheck server logs after paymentWebhook logged and processed
4Order status updatedCheck database after paymentStatus = "paid"
5Confirmation message sentCheck Telegram after paymentCustomer receives confirmation

Error Path (Failed Payments)

← Swipe β†’
#Test CaseHow to TestExpected Result
6Declined cardUse test card 4000...0002Friendly error message, no charge
7Insufficient fundsUse test card 4000...9995Friendly error message
83D Secure requiredUse test card 4000...31553DS flow works, payment completes
9Customer cancels paymentTap cancel on Stripe pageReturn to bot, order preserved
10Network timeoutSimulate slow connectionBot handles gracefully, no crash

Edge Cases

← Swipe β†’
#Test CaseHow to TestExpected Result
11Double webhook deliveryReplay webhook from Stripe dashboardNo double charge (idempotency)
12Price change during checkoutChange price in DB while customer paysServer-side validation catches mismatch
13Out of stock after cartRemove product after add-to-cartError at checkout, not at payment
14Refund processingIssue refund via Stripe, check botOrder status = "refunded", customer notified
15Concurrent paymentsTwo users pay for last item simultaneouslyOnly one succeeds, other gets error

Test Card Numbers (Stripe)

← Swipe β†’
Card NumberScenarioWhat Should Happen
4242 4242 4242 4242SuccessPayment completes
4000 0000 0000 0002DeclinedError message shown
4000 0000 0000 9995Insufficient fundsError message shown
4000 0025 0000 31553D Secure3DS popup, then success
4000 0000 0000 0341Failed (attach)Attach error

Do not skip testing. I once caught a bug where the bot showed the wrong total after a cart modification β€” the customer would have been charged $45 instead of $35. One hour of testing saved a refund, a support ticket, and a bad review.

Common Payment Integration Errors (And How to Fix Them)

These are the errors I see developers hit most often when integrating payments into Telegram bots. Each one has a simple fix β€” if you know where to look.

← Swipe β†’
ErrorCauseFix
"Provider token not found"Wrong token or not configured in BotFatherRe-configure payments in @BotFather
Webhook not receiving eventsWrong URL or HTTPS not configuredUse ngrok for testing, verify URL in Stripe
"No such price"Amount in wrong formatUse cents: $10.00 = 1000, not 10
Payment form not showingProvider token is for wrong countryMatch provider country to your Stripe account
"Invoice not found"Invoice expired (24h limit)Create fresh invoice when customer is ready
Double chargesNo idempotency checkUse transaction ID as unique key
Refund not workingUsing test key in productionCheck you are using live keys
Currency mismatchBot sends USD, Stripe expects EURMatch currency in bot code and Stripe settings

Error #1: "Provider Token Not Found"

This is the most common error for beginners. It means Telegram cannot find your payment provider configuration.

Fix: 1. Open @BotFather 2. Select your bot β†’ Payments 3. Choose a provider (e.g., Stripe) 4. Follow the setup flow 5. Copy the provider token 6. Use it in your code

Error #3: Amount in Wrong Format

Stripe and Telegram Payments both expect amounts in the smallest currency unit: - USD: $10.00 β†’ 1000 (cents) - EUR: €10.00 β†’ 1000 (cents) - JPY: Β₯1000 β†’ 1000 (yen, no decimals)

Common mistake: sending 10.00 instead of 1000. This results in a $0.10 charge instead of $10.00.

Error #6: Double Charges

If your webhook handler does not check for duplicates, a retry (Stripe retries failed webhooks for up to 3 days) will process the payment again.

Fix: store the Stripe event ID in your database and check before processing:

Python
 1# Protection against duplicate webhook processing
 2# Stripe may send webhook multiple times on retry
 3
 4async def handle_webhook(event_id: str, event_type: str, data: dict):
 5    """
 6    Processes webhook with duplicate protection.
 7    """
 8    # Check β€” have we seen this event before?
 9    if await db.event_exists(event_id):
10        # Already processed β€” skip
11        return {"status": "already_processed"}
12
13    # Save event ID to DB (before processing!)
14    await db.save_event(event_id, event_type)
15
16    # Now safe to process
17    if event_type == "checkout.session.completed":
18        await process_payment(data)
19
20    return {"status": "processed"}

Pro tip: always test with Stripe's test mode first. Switch to live keys only after all 15 test cases pass. I have seen developers accidentally charge real customers during testing because they used live keys too early.

Cost Comparison: Which Payment Method Is Cheapest for Your Volume?

Transaction fees add up. Here is exactly how much each payment method costs at different monthly volumes β€” so you can make an informed decision.

Fee Comparison by Monthly Volume

← Swipe β†’
Monthly RevenueStripe FeesPayPal FeesTelegram + StripeSavings vs PayPal
$1,000$59$74$59$15/month
$5,000$245$324$245$79/month
$10,000$490$649$490$159/month
$25,000$1,025$1,522$1,025$497/month
$50,000$1,950$2,945$1,950$995/month
$100,000$3,900$5,890$3,900$1,990/month

Stripe: 2.9% + $0.30/transaction. PayPal: 3.49% + $0.49/transaction. Assuming average transaction of $35.

The Hidden Cost: Failed Payments

Not all payment attempts succeed. Here is the real cost including failed attempts:

← Swipe β†’
ProviderSuccess RateEffective Fee (per successful txn)
Stripe95%3.05% + $0.32
PayPal92%3.79% + $0.53
Telegram Payments (Stripe)97%2.99% + $0.31

Telegram Payments has the highest success rate because the customer never leaves the app β€” no redirect means fewer abandoned payments.

Development Cost Comparison

← Swipe β†’
ApproachDev TimeDev CostMonthly Maintenance
Stripe Checkout (redirect)2–3 days$500 – $1,000$0 (Stripe handles it)
Stripe + Telegram Payments3–5 days$800 – $1,500$0
PayPal integration3–5 days$800 – $1,500$0
Both Stripe + PayPal5–7 days$1,200 – $2,500$0
Custom payment flow2–4 weeks$3,000 – $8,000$100 – $300

My Recommendation by Business Type

← Swipe β†’
Business TypeRecommended SetupWhy
Small store (< $5K/month)Telegram Payments + StripeLowest friction, fastest setup
Medium store ($5K–$50K/month)Stripe Checkout + PayPal optionFlexibility, better analytics
Subscription businessStripe SubscriptionsOnly Stripe handles recurring well
International salesStripe + PayPal + CryptoCover all payment preferences
High-ticket ($500+)Stripe + PayPal (buyer protection)Trust signal for expensive items

Total Cost of Ownership (First Year)

← Swipe β†’
ComponentStripe OnlyStripe + PayPalAll Three
Development$800 – $1,500$1,200 – $2,500$1,500 – $3,000
Transaction fees ($10K/month)$5,880/year$5,880/year$5,880/year
Hosting$120/year$120/year$120/year
Maintenance$0$0$0
Total Year 1$6,800 – $7,500$7,200 – $8,500$7,500 – $9,000

The difference between payment methods is small compared to the revenue they enable. Do not overthink it β€” start with Stripe, add PayPal if customers ask for it.

I cover the full picture of bot costs β€” including payment integration β€” in my telegram bot development cost guide. And if you want to see how payments work in a real e-commerce context, check my Telegram bot for e-commerce case study.

πŸ’¬ Ready to add payments to your Telegram bot? I have integrated Stripe, PayPal, and Telegram Payments into 20+ production bots. Describe your product and I will recommend the best payment setup β€” bots from $500, delivered in 5-7 days β†’

πŸ”— Related Resources
E-commerce bot with payment integrationTelegram bot for e-commerce
Full cost breakdown including payment integrationtelegram bot development cost
Real projects with payment integration resultsTelegram bot case studies
Find a developer who knows payment integrationhire a Telegram bot developer
Choose the right framework for your payment botbest Telegram bot frameworks

Frequently Asked Questions

Answers to the most popular questions about telegram bot payments integration

Need payments in your Telegram bot?

I have integrated Stripe, PayPal, and Telegram Payments into 20+ production bots. Describe your product and I will set up the best payment flow for your business.

πŸ’¬