How to build a simple Discord bot that creates a Stripe Checkout session on startup and posts the payment link to a channel.
Tags: python, stripe, discord-bot, ai-saas
TL;DR
-
Set environment variables –
STRIPE_SECRET_KEY,DISIPE_PRICE_ID_PRO,DISCORD_BOT_TOKEN,DISCORD_CHANNEL_ID. - Run the script – it creates a Stripe Checkout session for the “CIEL PRO” plan (USD $29) and posts the link to your Discord channel.
- Optional – expose a webhook with ngrok to listen for successful payments and react (e.g., assign a role).
Below you’ll find a ready‑to‑run Python file, a tiny README, and a short test routine. No extra dependencies beyond stripe and discord.py are required.
1. The full source – core/autonomous/stripe_checkout_discord_bot.py
# core/autonomous/stripe_checkout_discord_bot.py
"""
Discord bot that creates a Stripe Checkout session on startup
and posts the payment link to a pre‑configured channel.
Environment variables required:
STRIPE_SECRET_KEY – Your Stripe secret test key.
STRIPE_PRICE_ID_PRO – The price ID for the $29 “CIEL PRO” plan.
DISCORD_BOT_TOKEN – Discord bot token.
DISCORD_CHANNEL_ID – Numeric ID of the channel to post to.
"""
import os
import re
import logging
import asyncio
import stripe
import discord
from discord.ext import commands
# --------------------------------------------------------------------------- #
# Logging configuration
# --------------------------------------------------------------------------- #
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s | %(message)s",
)
log = logging.getLogger(__name__)
# --------------------------------------------------------------------------- #
# Load configuration from the environment
# --------------------------------------------------------------------------- #
STRIPE_SECRET_KEY = os.getenv("STRIPE_SECRET_KEY")
STRIPE_PRICE_ID_PRO = os.getenv("STRIPE_PRICE_ID_PRO")
DISCORD_BOT_TOKEN = os.getenv("DISCORD_BOT_TOKEN")
DISCORD_CHANNEL_ID = os.getenv("DISCORD_CHANNEL_ID")
# Basic sanity checks – fail fast if something is missing
missing = [
var_name
for var_name, value in [
("STRIPE_SECRET_KEY", STRIPE_SECRET_KEY),
("STRIPE_PRICE_ID_PRO", STRIPE_PRICE_ID_PRO),
("DISCORD_BOT_TOKEN", DISCORD_BOT_TOKEN),
("DISCORD_CHANNEL_ID", DISCORD_CHANNEL_ID),
]
if not value
]
if missing:
raise EnvironmentError(f"Missing required env vars: {', '.join(missing)}")
# Convert channel ID to int (Discord expects an integer)
DISCORD_CHANNEL_ID = int(DISCORD_CHANNEL_ID)
# Initialise Stripe
stripe.api_key = STRIPE_SECRET_KEY
# --------------------------------------------------------------------------- #
# Helper: create a Stripe Checkout session
# --------------------------------------------------------------------------- #
def create_checkout_session(product_price_id: str) -> str:
"""
Create a Stripe Checkout session for a single‑product price.
Returns the URL that the customer should be redirected to.
"""
try:
session = stripe.checkout.Session.create(
payment_method_types=["card"],
line_items=[{
"price": product_price_id,
"quantity": 1,
}],
mode="payment",
success_url="https://example.com/success?session_id={CHECKOUT_SESSION_ID}",
cancel_url="https://example.com/cancel",
)
log.info("Created Stripe Checkout session %s", session.id)
return session.url
except stripe.error.StripeError as exc:
log.exception("Stripe API error while creating checkout session")
raise RuntimeError("Failed to create Stripe checkout session") from exc
# --------------------------------------------------------------------------- #
# Discord bot definition
# --------------------------------------------------------------------------- #
intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)
@bot.event
async def on_ready():
"""Runs once the bot has connected to Discord."""
log.info("Discord bot connected as %s", bot.user)
# Create the checkout URL
try:
checkout_url = create_checkout_session(STRIPE_PRICE_ID_PRO)
except Exception as exc:
await bot.get_channel(DISCORD_CHANNEL_ID).send(
"❌ Oops – could not generate a payment link. Check the logs."
)
return
# Validate URL format (basic sanity check)
if not re.match(r"^https://checkout\.stripe\.com/.+$", checkout_url):
log.warning("Unexpected checkout URL format: %s", checkout_url)
await bot.get_channel(DISCORD_CHANNEL_ID).send(
"⚠️ Received an unexpected checkout link, please investigate."
)
return
# Post the message
message = (
f"🚀 Grab the **CIEL PRO** plan for **$29**: {checkout_url}\n"
"Your AI‑powered toolkit awaits! ✨"
)
channel = bot.get_channel(DISCORD_CHANNEL_ID)
if channel:
await channel.send(message)
log.info("Posted checkout link to channel %s", DISCORD_CHANNEL_ID)
else:
log.error("Could not find channel ID %s", DISCORD_CHANNEL_ID)
# --------------------------------------------------------------------------- #
# Optional: webhook endpoint (run with ngrok)
# --------------------------------------------------------------------------- #
# The webhook part is optional – you can spin up a tiny Flask app in another
# file if you need real‑time payment confirmation. The code below is a stub
# that demonstrates the pattern without pulling in Flask as a hard dependency.
#
# def start_webhook():
# from flask import Flask, request, abort
# app = Flask(__name__)
#
# @app.route("/stripe/webhook", methods=["POST"])
# def stripe_webhook():
# payload = request.data
# sig_header = request.headers.get("Stripe-Signature")
# endpoint_secret = os.getenv("STRIPE_WEBHOOK_SECRET")
# try:
# event = stripe.Webhook.construct_event(
# payload, sig_header, endpoint_secret
# )
# except (ValueError, stripe.error.SignatureVerificationError):
# abort(400)
#
# if event["type"] == "checkout.session.completed":
# session = event["data"]["object"]
# log.info("âś… Payment succeeded for session %s", session["id"])
# # Here you could add a Discord role or send a DM, etc.
#
# return "", 200
#
# # Run under ngrok (you must have ngrok installed locally)
# # ngrok http 5000
# app.run(port=5000)
# --------------------------------------------------------------------------- #
# Simple test routine – can be executed with `python -m core.autonomous.stripe_checkout_discord_bot`
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
# Quick sanity test for the checkout URL format
try:
test_url = create_checkout_session(STRIPE_PRICE_ID_PRO)
assert re.match(r"^https://checkout\.stripe\.com/.+$", test_url), "Invalid URL"
log.info("âś… Checkout URL looks good: %s", test_url)
except Exception as exc:
log.error("Test failed: %s", exc)
raise SystemExit(1)
# Start the Discord bot (this will block)
try:
bot.run(DISCORD_BOT_TOKEN)
except discord.DiscordException as exc:
log.exception("Discord client error")
raise SystemExit(1)
What the script does
| Step | Description |
|---|---|
| Load env vars | Reads all secrets from the OS environment – never hard‑codes keys. |
| Create checkout session | Calls stripe.checkout.Session.create with the supplied price ID (the $29 CIEL PRO plan). |
| Validate URL | Simple regex ensures we got a Stripe checkout link. |
| Post to Discord | Sends a nicely formatted message to the channel you configured. |
| Test mode | When run directly (python -m …), the script creates a session |
Top comments (0)