To give a Discord role when someone pays with Stripe, you need three pieces: a way to know the buyer’s Discord account, a webhook that listens for Stripe’s subscription events, and a bot that adds or removes the role through Discord’s API. You can build all three yourself, or use a bot like flatpass that runs the same setup on your own Stripe account.
Five steps to a working first version.
Stripe knows an email address, not a Discord account, and the two often don’t match. So link them before checkout:
identify scope. You get their Discord user ID.guilds.join if buyers may not be in your server yet. It lets your bot add them.client_reference_idand on the subscription’s metadata, so every later event can find it.The bot doesn’t need to be online in Discord’s gateway to change roles. Its token works with Discord’s REST API, called from your webhook handler.
A Stripe Checkout Session in subscription mode, with the price for the role and the Discord user ID from step 1. Stripe hosts the page, so cards, Apple Pay and Google Pay come with it.
Turn on Stripe’s customer portal for cancelling and changing cards. Otherwise every cancellation is a message to you.
A webhook endpoint on an always-on HTTPS server. Two rules before any logic:
Then map each event to what happens to the role:
| Stripe event | The role |
|---|---|
| checkout.session.completed | Add the role, and add the member to the server if needed. |
| customer.subscription.updated | Keep the role while the status is active, trialing or past_due; remove it on unpaid, canceled or paused. |
| customer.subscription.deleted | Remove the role. |
| charge.refunded | Your call: most remove the role on a full refund and cancel the subscription. |
| charge.dispute.created | Your call: most remove the role until the dispute is decided. |
Keeping the role through past_due matters: Stripe retries a failed renewal for days, and removing the role on the first failed charge punishes someone whose card simply expired.
Three calls, with your bot token:
Add a rolePUT /guilds/{guild}/members/{user}/roles/{role}
Remove a roleDELETE /guilds/{guild}/members/{user}/roles/{role}
Add to serverPUT /guilds/{guild}/members/{user}
Adding someone to the server takes the OAuth2 access token from step 1, and the role can go in the same call. Then handle what Discord sends back: 50013means the role is above the bot’s, so retrying won’t help; 10007means they aren’t in the server; 429 means wait for retry_after and try again.
The five steps make a working first version. These make one you can leave running.
A member who cancels has paid until the period ends. Remove the role when the subscription actually ends, not when they click Cancel.
Discord drops a member’s roles when they leave. If they come back while still paying, nothing gives the role back unless you listen for joins (the privileged Server Members intent) or give them a way to ask.
Someone drags the role you sell above the bot’s, and every grant of it fails from then on. You need to notice, and re-grant the members who paid in the meantime.
Your server was down for longer than Stripe retries. Without a periodic check of Stripe against Discord, those members never get their role, or never lose it.
Map each price to a role, and swap roles when a member moves between tiers.
Which member holds which subscription, so you can answer “I paid and have no role” without opening two dashboards.
What building it costs
Your time, for the first version and for the edge cases. An always-on HTTPS server for the webhook. Upkeep as Stripe’s and Discord’s APIs change. And no platform fee: only Stripe’s card processing. If you want full control or custom logic, it’s a good project.
flatpass is this setup, already built, running on your own Stripe account.
Linking the Discord account. Members sign in with Discord before checkout, with guilds.join, so a buyer who isn’t in your server gets added.
The bot. Two permissions, Manage Roles and Create Invite. Never Administrator.
The checkout. Stripe Checkout on your own Stripe account, with cards, Apple Pay and Google Pay.
Webhook safety. Every Stripe event is signature-checked and processed once.
Failed renewals. The role stays while Stripe retries the card.
Cancellations. The role stays until the paid period ends. Members cancel in Stripe’s portal, from /membership.
Refunds. A full refund removes the role and ends the subscription; a partial one changes nothing.
Chargebacks. The role comes off when a dispute opens. It comes back if you win, and the subscription ends if you lose.
The role order. Your dashboard warns you, and members who paid in the meantime get their role once you fix it.
Leaving and rejoining. /membership, or the Open button on your invite page, puts the role back.
Your records. A member list with each member’s tier, status, renewal date and whether the role is on.
Not built yet: moving a member between tiers, and one-time payments. Free up to 15 paid members, then a flat monthly fee from $29, and 0% of your sales; Stripe’s card fee is the same either way. How it works for your members.
To give a Discord role when someone pays with Stripe, you need three pieces: a way to know the buyer’s Discord account, a webhook that listens for Stripe’s subscription events, and a bot that adds or removes the role through Discord’s API. You can build all three yourself, or use a bot like flatpass that runs the same setup on your own Stripe account.
Not a built-in one. Stripe takes the payment, and something else has to change the Discord role: your own webhook and bot, or an app that does it for you.
Yes. The same pieces apply: the Discord account link before checkout, the Stripe events to handle, and the Discord calls. The edge cases don’t go away.
Yes, for the webhook. Stripe has to be able to reach it at any time over HTTPS.
Not to change roles: the API calls work with the bot’s token alone. Only to react to things happening in Discord, like a member rejoining.
Free up to 15 paid members · 0% of your sales · Your own Stripe account