# Slootly — Subscription Infrastructure for Messaging Bots # https://slootly.dev # Version: 0.1 ## What is Slootly? Slootly adds subscription payments to Telegram, Discord, WhatsApp, and Slack bots. One SDK, every platform. Revenue goes directly to the developer's bank account via Stripe. No crypto, no Stars, no complex cash-out paths. ## Install ```bash # TypeScript npm install @slootly/sdk # Python pip install slootly ``` ## Quick Start — TypeScript ```typescript import { Slootly } from '@slootly/sdk'; const gate = new Slootly('sk_live_your_api_key', { projectSlug: 'your-project' }); // Check if a user is subscribed const sub = await gate.check(userId, 'telegram'); // sub.active — boolean, true if subscribed // sub.plan — string, e.g. 'pro' // sub.status — 'active' | 'trialing' | 'past_due' | 'canceled' | 'expired' | 'none' if (sub.active && sub.plan === 'pro') { // Premium feature } else { // Generate checkout link — synchronous, no API call const url = gate.checkoutUrl(userId, 'pro', 'telegram'); bot.sendMessage(chatId, `Upgrade to Pro: ${url}`); } ``` ## Quick Start — Python ```python from slootly import Slootly gate = Slootly("sk_live_your_api_key", project_slug="your-project") # Check if a user is subscribed sub = await gate.check(user_id, "telegram") # sub.active — bool # sub.plan — str, e.g. "pro" # sub.status — "active" | "trialing" | "past_due" | "canceled" | "expired" | "none" if sub.active and sub.plan == "pro": # Premium feature pass else: # Generate checkout link — synchronous, no API call url = gate.checkout_url(user_id, "pro", "telegram") await bot.send_message(chat_id, f"Upgrade to Pro: {url}") ``` ## SDK Methods ### TypeScript ```typescript // Initialize — pass API key string or config object const gate = new Slootly('sk_live_...', { projectSlug: 'my-project' }); // or const gate = new Slootly({ apiKey: 'sk_live_...', projectSlug: 'my-project', cacheTtlMs: 300_000, // cache TTL in ms (default: 5 min) enableJwtVerification: true, // offline JWT verification (default: true) timeout: 5_000, // HTTP timeout in ms (default: 5s) }); // Check subscription status (async) const sub = await gate.check(userId: string, platform: Platform): Promise; // Platform = 'telegram' | 'discord' | 'whatsapp' | 'slack' | 'custom' // Generate checkout URL (synchronous) const url = gate.checkoutUrl(userId: string, planSlug: string, platform: Platform, options?: CheckoutOptions): string; // options: { successUrl?, cancelUrl?, email?, metadata? } // Fetch all plans (async) const plans = await gate.getPlans(): Promise; // Cancel subscription (async) await gate.cancelSubscription(subscriberId: string): Promise; // Webhook middleware for Express app.post('/webhooks', gate.onEvent((event: WebhookEvent) => { // event.type: 'subscription.created' | 'subscription.renewed' | 'subscription.canceled' | 'payment.failed' // event.data.subscriberId, event.data.plan, event.data.status })); // Manual webhook verification const event = gate.verifyWebhook(payload: string, signature: string, secret: string): WebhookEvent; // Cache management gate.clearCache(); gate.destroy(); // cleanup on shutdown ``` ### Python ```python # Initialize gate = Slootly("sk_live_...", project_slug="my-project", cache_ttl=300, offline_verification=True, timeout=5.0) # Check subscription status (async) sub = await gate.check(user_id: str, platform: str) -> Subscription # Generate checkout URL (synchronous) url = gate.checkout_url(user_id, plan_slug, platform, success_url=None, cancel_url=None, email=None, metadata=None) -> str # Fetch all plans (async) plans = await gate.get_plans() -> List[Plan] # Cancel subscription (async) await gate.cancel_subscription(subscriber_id: str) # Verify webhook event = gate.verify_webhook(payload: str, signature: str, secret: str) -> WebhookEvent # Cleanup gate.clear_cache() await gate.close() # Async context manager async with Slootly("sk_live_...") as gate: sub = await gate.check(user_id, "telegram") ``` ## Subscription Object ```typescript interface Subscription { active: boolean; // true if user has active subscription plan: string | null; // plan slug, e.g. 'pro' planId: string | null; // plan ID status: SubscriptionStatus; // 'active' | 'trialing' | 'past_due' | 'canceled' | 'expired' | 'none' currentPeriodEnd: Date | null; cancelAtPeriodEnd: boolean; trialEnd: Date | null; subscriberId: string | null; platforms: Platform[]; // linked platforms metadata: Record; } ``` ## Common Patterns ### Telegram Bot (node-telegram-bot-api) ```typescript import TelegramBot from 'node-telegram-bot-api'; import { Slootly } from '@slootly/sdk'; const bot = new TelegramBot(process.env.BOT_TOKEN, { polling: true }); const gate = new Slootly('sk_live_...'); bot.on('message', async (msg) => { const userId = msg.from.id.toString(); const sub = await gate.check(userId, 'telegram'); if (sub.active && sub.plan === 'pro') { bot.sendMessage(msg.chat.id, 'Premium content here!'); } else { const url = gate.checkoutUrl(userId, 'pro', 'telegram'); bot.sendMessage(msg.chat.id, 'Upgrade to Pro:', { reply_markup: { inline_keyboard: [[{ text: 'Subscribe $9.99/mo', url }]] } }); } }); ``` ### Discord Bot (discord.js) ```typescript import { Client, GatewayIntentBits } from 'discord.js'; import { Slootly } from '@slootly/sdk'; const client = new Client({ intents: [GatewayIntentBits.Guilds] }); const gate = new Slootly('sk_live_...'); client.on('interactionCreate', async (interaction) => { if (!interaction.isChatInputCommand()) return; const userId = interaction.user.id; const sub = await gate.check(userId, 'discord'); if (sub.active) { await interaction.reply({ content: 'Premium content!', ephemeral: true }); } else { const url = gate.checkoutUrl(userId, 'pro', 'discord'); await interaction.reply({ content: `Subscribe: ${url}`, ephemeral: true }); } }); ``` ### Python Telegram Bot (python-telegram-bot) ```python from telegram import Update from telegram.ext import ApplicationBuilder, MessageHandler, filters from slootly import Slootly gate = Slootly("sk_live_...") async def handle(update: Update, context): user_id = str(update.effective_user.id) sub = await gate.check(user_id, "telegram") if sub.active and sub.plan == "pro": await update.message.reply_text("Premium content!") else: url = gate.checkout_url(user_id, "pro", "telegram") await update.message.reply_text(f"Upgrade: {url}") app = ApplicationBuilder().token("BOT_TOKEN").build() app.add_handler(MessageHandler(filters.TEXT, handle)) app.run_polling() ``` ## Key Facts - API keys start with `sk_live_` (production) or `sk_test_` (testing) - Checkout URLs are generated locally (no API call needed) - SDK caches results and uses JWT offline verification for speed - Works across platforms: same subscription, same code - Webhook events: subscription.created, subscription.renewed, subscription.canceled, payment.failed - Stripe Connect Standard: money goes directly to developer's Stripe account ## Links - Docs: https://slootly.dev/docs - TypeScript SDK: https://slootly.dev/docs/typescript - Python SDK: https://slootly.dev/docs/python - API Reference: https://slootly.dev/docs/api - Full LLM docs: https://slootly.dev/llms-full.txt