Guides
Recipe: Telegram bot
Sell bot commands, a role or a private group. Each user gets their own invoice; a signed webhook unlocks them.
The bot never sees money. On /buy it sends the user their own invoice link; your server hears invoice.fulfilled and unlocks. Every paid command asks Perly Pay, so a restart loses nothing. Discord works the same way, with a role.
sell-bot-access skill of the Perly Pay plugin for Claude Code does this recipe in your project — the plugin and its example, examples/telegram-bot (MIT), are on their way. Until then, any assistant can read the same skill from /.well-known/skills/perly-pay-sell-bot-access/SKILL.md.How it works
- 1
A product and price
One-time or lifetime for "forever", fixed term for passes, subscription for memberships renewed by invoice.
- 2
A customer per chat user
external_id: "tg:<user id>". Ask for an email only for subscriptions: renewals go there. - 3
/buy
An invoice with the chat id in
metadata, sent as a button. One invoice per user per day, by idempotency key. - 4
Unlock
On
invoice.fulfilled, read the chat id frommetadataand answer: a message, a one-time invite link, a role. - 5
Paid commands
entitlements.check({ customer_external_id: "tg:<id>", product }).
Run the example
- 1
Install
npm installinexamples/telegram-bot(Node 20.12+). - 2
Key
Copy
.env.exampleto.envand put a test key (sk_test_…) inPERLY_PAY_SECRET_KEY. - 3
Set up
npm run setupmakes the product and price with your key and prints their ids for.env. - 4
Run
npm start(ornpm run dev), as its README says. - 5
Pay without a wallet
Message the bot
/buy, open Pay and press Simulate payment: the bot says "Payment confirmed" and/secretanswers.
Pitfalls
- One shared payment link for everyone: you cannot tell who paid. An invoice per user, with the chat id.
- Unlocking when the user taps "I paid". Only the webhook or
entitlements.checkunlocks. - Access kept in the bot's memory. It is gone after a restart; ask Perly Pay.