Skip to content
CreateDiscordBot
Menu

How to sell Discord roles with Stripe, and what each option costs

How to sell Discord roles with Stripe: LaunchPass, Whop, Discord's own subscriptions or your own bot, with real fees at 50 and 300 members and tested code.

By Adam Peleback. Updated . 13 min read.

On this page
  1. Ways to sell Discord roles, and which fits your server
  2. What each option costs at 50 and 300 members
  3. Discord's own options, and the rule that affects bots
  4. How a Stripe-to-Discord role bot works
  5. The core code: Stripe webhook to Discord role
  6. Test it end to end in Stripe test mode
  7. Setup checklist
  8. When to hand it off

To sell Discord roles with Stripe, you connect a Stripe payment to a bot that is allowed to manage roles in your server. Either a hosted tool does that on top of your Stripe account (LaunchPass, Upgrade.chat), or you run a small bot of your own that listens to Stripe's webhooks and adds or removes the role. Whop and Patreon take the payment themselves and run their own role bots, and Discord's Server Subscriptions let members pay inside Discord, but only for server owners based in the US.

The routes differ more on fees than on features. At 50 members paying $10 a month, you would pay between $32 and $79.50 a month in fees depending on the route, as of September 2026. Below are the numbers with the arithmetic, the Discord policy question to check before you build, and a tested webhook-to-role sketch you can run yourself.

Ways to sell Discord roles, and which fits your server

If you... Use
Want it live today with no code or hosting LaunchPass or Whop
Want the lowest per-payment fee without writing code Whop
Want payments to land in your own PayPal and Stripe accounts Upgrade.chat
Already publish on Patreon and your members are there Patreon's Discord integration
Are US-based and want members to pay inside Discord Discord Server Subscriptions
Want your own Stripe customer list, custom rules, or several products and servers Your own bot

LaunchPass has a free plan, but it cannot charge members, so the paid Premium plan is the one to compare.

What each option costs at 50 and 300 members

The fee schedules

All figures are for US card payments as of September 2026, taken from each company's own pricing page (linked in the sources).

Option Per-payment fee Fixed monthly
Your own Stripe bot Stripe 2.9% + 30¢, plus Stripe Billing 0.7% (pay-as-you-go) $0, plus hosting
Whop 2.7% + 30¢, plus $0.07 Radar per transaction $0
LaunchPass Premium 3.5%, plus Stripe 2.9% + 30¢ $29
Upgrade.chat Free 5.9%, plus your Stripe 2.9% + 30¢ (up to 500 subscribers) $0
Upgrade.chat Pro / VIP / Max 4.9% / 3.9% / 2.9%, plus your Stripe fees (up to 1,000 / 2,500 / unlimited subscribers) $19 / $49 / $199
Patreon (pages published after Aug 4, 2025) 10%, plus 2.9% + 30¢ processing $0
Discord Server Subscriptions (desktop and web) 6% processing, then 10% of what remains $0
Discord Premium Apps (for a bot's own features) 6% processing, then 15% of what remains on the first $1M $0

Upgrade.chat takes payments into your own Stripe or PayPal account, so those accounts' processing fees come on top of its percentage. I used Stripe's card rate for the math.

The results

Option Fee per $10 payment 50 members ($500/mo) 300 members ($3,000/mo)
Whop $0.64 $32.00 $192.00
Your own Stripe bot $0.66 $33.00 + hosting $198.00 + hosting
Upgrade.chat Free $1.18 $59.00 $354.00
Upgrade.chat Pro $1.08 + $19/mo $73.00 $343.00
LaunchPass Premium $0.94 + $29/mo $76.00 $311.00
Discord Server Subscriptions $1.54 $77.00 $462.00
Patreon $1.59 $79.50 $477.00
Discord Premium Apps (reference) $2.01 $100.50 $603.00

The arithmetic

Each line is one $10 payment, then multiplied by 50 and 300 members.

  • Your own Stripe bot: $10 × 2.9% = $0.29, plus $0.30, plus $10 × 0.7% = $0.07, so $0.66. × 50 = $33.00. × 300 = $198.00.
  • Whop: $10 × 2.7% = $0.27, plus $0.30, plus $0.07, so $0.64. × 50 = $32.00. × 300 = $192.00.
  • LaunchPass: $10 × 3.5% = $0.35, plus Stripe's $0.59, so $0.94. × 50 = $47.00 + $29 = $76.00. × 300 = $282.00 + $29 = $311.00.
  • Upgrade.chat Free: $10 × 5.9% = $0.59, plus Stripe's $0.59, so $1.18. × 50 = $59.00. × 300 = $354.00.
  • Upgrade.chat Pro: $0.49 + $0.59 = $1.08. × 50 = $54.00 + $19 = $73.00. × 300 = $324.00 + $19 = $343.00.
  • Patreon: $10 × 10% = $1.00, plus $0.29 + $0.30, so $1.59. × 50 = $79.50. × 300 = $477.00.
  • Discord Server Subscriptions: processing $10 × 6% = $0.60, then 10% of the remaining $9.40 = $0.94, so $1.54. × 50 = $77.00. × 300 = $462.00.
  • Discord Premium Apps: $0.60 processing, then 15% of $9.40 = $1.41, so $2.01, leaving the developer $7.99.

Upgrade.chat's Pro plan beats its free plan above $1,900 a month in sales. VIP ties Pro at $3,000 a month (300 members at $10) and beats it above that, and Max beats VIP above $15,000. The subscriber caps can force an upgrade sooner: 500 on the free plan and 1,000 on Pro.

What the table leaves out

  • International cards and currency conversion. Stripe and Whop both add 1.5% for international cards and 1% when currency is converted. Patreon charges 2.5% when a member pays in a different currency from your payout currency.
  • Disputes. Stripe charges $15 per dispute received and $15 more if you counter it; the second fee is refunded if you win. Whop charges $15 per dispute.
  • Payouts. Whop charges $2.50 per next-day ACH payout and Patreon $0.25 per US bank payout. Discord pays monthly once your server has earned $100, then whenever at least $25 more has built up.
  • Mobile. Server Subscriptions bought in Discord's iOS app cost the member more, because Apple's fee is added to the price. They can't be bought on Android at all.
  • Other listed fees. Whop lists $0.03 per 3D Secure check and adds 0.5% when its Billing automation is enabled. Stripe Tax Basic's no-code option adds 0.5% per transaction where you are registered to collect tax.
  • Stripe Billing on hosted tools. LaunchPass and Upgrade.chat charge through your Stripe account. LaunchPass lists only Stripe's card fee, neither says whether Stripe's 0.7% Billing fee also applies, and I couldn't confirm it either way. Check your first Stripe invoice.

Where your own bot breaks even

On fees alone, your own Stripe bot costs about the same as Whop: $0.66 against $0.64 per $10 payment. It only beats Whop below about $7.80 a payment, where Whop's flat 7¢ Radar fee outweighs Stripe's higher percentage, so the reasons to build are control, custom rules and owning the Stripe account.

Against LaunchPass it saves $0.28 per payment plus the $29 subscription: $43 a month at 50 members and $113 at 300. Against Patreon it saves $0.93 per payment, or $46.50 a month at 50 members. Hosting runs about $3 to $5 a month: Railway's Hobby plan is $5 including $5 of usage and can give the webhook an HTTPS domain, while a $3 bot host such as PebbleHost's 1 GB plan runs the bot but may not serve HTTPS. Weigh that saving against the time to build and maintain the bot, or what a developer charges. The Discord bot cost guide has real price ranges.

Discord's own options, and the rule that affects bots

Server Subscriptions

Server Subscriptions let members pay inside Discord for 1 to 3 monthly tiers that open up paid channels or the whole server. Owners must be 18 or older, have 2FA on, run a Community server and provide US banking and ID to Stripe. Discord's Creator Revenue FAQ says they will not be available outside the United States.

Discord keeps a 10% platform fee after a 6% processing fee on desktop and web. Free trials of one day or one week are built in. If you qualify, it is the lowest-effort option, and members never leave Discord.

Premium Apps

Premium Apps is Discord's store for a bot's own paid features: user subscriptions, server subscriptions and one-time purchases. Developers in the US, UK and EU keep 85% of what remains after processing on their first $1 million in sales, then 70%. The app must be verified and owned by a developer team. It is built for public bots selling features to many servers, not for a creator selling access to their own server.

The price-parity rule

Since October 7, 2024, Discord's Developer Policy has required apps that sell paid features, in regions where Premium Apps is supported, to also offer those features through Premium Apps at a final price no higher than anywhere else. Discord's FAQ says you can keep Stripe or Patreon alongside, but not at a lower price or with extra perks off Discord.

The policy and its FAQ talk about an app's own features: commands, content, enhanced functionality. A server selling access to its own community, with a bot only handing out the role, is not among Discord's examples, and I haven't found Discord guidance that addresses it directly. Read the current policy against what you sell, and if it is unclear, ask Discord developer support before you launch. When I build one of these, that check is part of the Blueprint.

How a Stripe-to-Discord role bot works

  1. The member signs in with Discord through OAuth2 with the identify scope, which gives you their Discord user ID. Add the guilds.join scope if you want the bot to put them into the server after payment.
  2. Your server creates a Stripe Checkout Session in subscription mode, with the Discord user ID in client_reference_id and in the subscription's metadata.
  3. Stripe sends checkout.session.completed. The bot verifies the signature, reads the subscription, and adds the role through Discord's Add Guild Member Role endpoint.
  4. Renewals and failures arrive as customer.subscription.updated. While the status is active, trialing or past_due, the role stays. When it turns unpaid or canceled, or customer.subscription.deleted arrives, the role comes off.
  5. A nightly job reconciles every active subscription against every member holding the role, to catch anything a missed webhook or a manual change left behind.

Linking Discord accounts to Stripe customers

This step decides whether the right person gets the role. There are three common ways, and one of them is clearly best:

  • Discord sign-in before checkout proves which Discord account is paying. The ID travels with the purchase, so there is nothing to match later.
  • Email matching, where the member types their Stripe email into a command, works if you verify the email. Without verification, anyone who knows a subscriber's email can claim their access.
  • Asking for a username at checkout is the weakest. Usernames change, get mistyped and can be someone else's.

Grace periods without writing a timer

You don't need your own countdown. When a renewal fails, Stripe marks the subscription past_due and retries the card. Smart Retries' recommended default is 8 tries within 2 weeks, and you can pick 1 week to 2 months. Keeping the role during past_due is your grace period.

Then check one setting. In Stripe's failed-payment settings you choose what happens after the last retry: cancel the subscription, mark it unpaid, or leave it past due. Pick cancel or unpaid. If you leave it past due, a role that follows the status never comes off.

The core code: Stripe webhook to Discord role

This sketch is the core of a Stripe-to-Discord role bot. It verifies Stripe's signature, reads the current subscription, and adds or removes one role through Discord's REST API, with no gateway connection needed. I tested it with stripe 22.6.2 (API version 2026-08-26.dahlia), discord.js 14.27.0 and Express 5.2.1 on Node 24.15.

terminal
npm install stripe discord.js express
.env
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
DISCORD_TOKEN=your-bot-token
GUILD_ID=your-server-id
MEMBER_ROLE_ID=the-paid-role-id
STRIPE_PRICE_ID=price_...

First, the checkout. This function runs after the member has signed in with Discord, and the ID it receives must come from Discord's API, never from a form field.

checkout.js
// Runs after the member has signed in with Discord (OAuth2, identify scope).
// discordUserId comes from GET /users/@me with their access token, never from a form field.
async function startCheckout(stripe, discordUserId) {
  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    line_items: [{ price: process.env.STRIPE_PRICE_ID, quantity: 1 }],
    client_reference_id: discordUserId,
    subscription_data: { metadata: { discord_user_id: discordUserId } },
    success_url: 'https://example.com/welcome',
    cancel_url: 'https://example.com/join',
  });
  return session.url; // redirect the member here
}

module.exports = { startCheckout };

Then the webhook. Putting the Discord ID on the subscription, not only on the Checkout Session, is what lets the cancellation event find the right member months later.

webhook.js
const express = require('express');
const Stripe = require('stripe');
const { REST, Routes, DiscordAPIError } = require('discord.js');

const { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, DISCORD_TOKEN, GUILD_ID, MEMBER_ROLE_ID } = process.env;
const stripe = new Stripe(STRIPE_SECRET_KEY);
const rest = new REST({ version: '10' }).setToken(DISCORD_TOKEN);

// past_due keeps the role while Stripe retries the card: that is the grace period.
const HAS_ACCESS = new Set(['active', 'trialing', 'past_due']);

async function syncRole(subscriptionId) {
  // Fetch the current subscription instead of trusting the event snapshot,
  // because Stripe does not guarantee that events arrive in order.
  const sub = await stripe.subscriptions.retrieve(subscriptionId);
  const discordUserId = sub.metadata.discord_user_id;
  if (!discordUserId) return;

  const route = Routes.guildMemberRole(GUILD_ID, discordUserId, MEMBER_ROLE_ID);
  const reason = `Stripe ${sub.id} is ${sub.status}`;
  try {
    if (HAS_ACCESS.has(sub.status)) await rest.put(route, { reason });
    else await rest.delete(route, { reason });
  } catch (err) {
    // 10007 Unknown Member: they are not in the server yet. The daily sync job retries.
    if (err instanceof DiscordAPIError && err.code === 10007) return;
    throw err;
  }
}

const app = express();

// express.raw keeps the body byte-for-byte, which signature verification needs.
app.post('/stripe/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, req.headers['stripe-signature'], STRIPE_WEBHOOK_SECRET);
  } catch (err) {
    return res.status(400).send(`Webhook signature check failed: ${err.message}`);
  }

  const obj = event.data.object;
  try {
    if (event.type === 'checkout.session.completed' && obj.mode === 'subscription') {
      await syncRole(obj.subscription);
    } else if (event.type === 'customer.subscription.updated' || event.type === 'customer.subscription.deleted') {
      await syncRole(obj.id);
    }
    res.sendStatus(200);
  } catch (err) {
    console.error(`Failed on ${event.id}`, err);
    res.sendStatus(500); // Stripe retries failed deliveries
  }
});

app.listen(process.env.PORT || 3000);

Run it with node --env-file=.env webhook.js, which Node has supported since version 20.6.

A few details matter more than they look:

  • The raw body. Stripe signs the exact bytes it sends. If a JSON parser touches the body first, verification fails, which is why this route uses express.raw instead of express.json.
  • The five-minute window. constructEvent also rejects signatures older than five minutes by default, which stops replayed requests. Keep your server's clock synced.
  • Idempotency. Adding a role a member already has, or removing one they don't, changes nothing, so a duplicate delivery is harmless. Stripe retries failed deliveries for up to three days in live mode, so duplicates do happen.
  • Ordering. Stripe doesn't guarantee event order. Reading the subscription fresh means an old event can't undo a newer state.
  • Discord's rules. The bot needs the Manage Roles permission, and its own role must sit above the paid role, because a bot can only grant roles below its highest role. The permissions calculator builds the invite link.

What I tested

I ran the files above exactly as published, with Stripe's generateTestHeaderString signing real payloads and the two network calls (Stripe's subscriptions.retrieve and Discord's REST put and delete) replaced by recorders, since nothing should call live APIs in a test. All 18 cases passed:

  • checkout.session.completed with an active subscription adds the role and passes a reason for Discord's audit log.
  • A Checkout Session in payment mode is ignored.
  • trialing and past_due keep the role; unpaid, canceled and incomplete remove it or never grant it.
  • An event whose snapshot says active, for a subscription that is now canceled, removes the role.
  • A duplicate delivery repeats the same request and returns 200.
  • A subscription without discord_user_id metadata is left alone.
  • A member who isn't in the server (Discord error 10007) returns 200 instead of three days of Stripe retries.
  • Any other Discord or Stripe error returns 500, so Stripe retries.
  • A tampered body, a wrong signing secret, a missing Stripe-Signature header and a ten-minute-old timestamp are all rejected with 400 before anything else runs, while a four-minute-old one is accepted.

I also ran checkout.js and the two Discord calls against local fake APIs to see the exact requests: the Checkout form fields, the role route, the X-Audit-Log-Reason header, and a 10007 response arriving as a DiscordAPIError. Both files type-check against stripe's bundled TypeScript types.

What the sketch leaves out

  • The nightly reconciliation job. List active subscriptions from Stripe and the members holding the role from Discord, then fix the difference. Listing members needs the Server Members privileged intent. Since June 10, 2026, apps that reach fewer than 10,000 unique users switch it on in the Developer Portal and larger ones apply for review.
  • An event log. Before adding side effects that aren't idempotent, such as a welcome message or an email, record each event.id in a table with a unique constraint and skip ones you have seen.
  • Members with more than one subscription. Before removing the role, check that the Stripe customer has no other active subscription.
  • Several tiers. Map each Stripe price ID to a role ID instead of using one MEMBER_ROLE_ID.
  • Adding people to the server. With the guilds.join scope, Discord's Add Guild Member endpoint can add the buyer with the role already set; the bot needs Create Instant Invite for that. Discord's Developer Policy requires telling people clearly and getting their permission before their account is added to a server.
  • Disputes. Listen for charge.dispute.created if you want access to end when a chargeback opens.

Test it end to end in Stripe test mode

  1. Run stripe listen --forward-to localhost:3000/stripe/webhook with the Stripe CLI. It prints a whsec_ secret for local testing; put that in .env and restart the webhook.
  2. Create a Checkout Session with checkout.js using test keys and pay with Stripe's test card 4242 4242 4242 4242. If that Discord account is in your test server, the role appears within seconds.
  3. To test a failed renewal, create a test clock in the Dashboard, then a customer on that clock with the card 4000 0000 0000 0341, which attaches but declines every charge. Give the customer a subscription with a short free trial and your test member's ID as discord_user_id metadata. Advance the clock past the trial: the charge fails, the status goes to past_due and the member has the role. Advance past the retry window and the status moves to canceled or unpaid, and the role comes off.

stripe trigger customer.subscription.deleted is less useful here than it looks. It creates a new test subscription without your discord_user_id metadata, so the sketch correctly ignores it.

Setup checklist

  • Discord: create the paid role, give the bot Manage Roles, and drag the bot's role above the paid role. If your server requires 2FA for moderation, the account that owns the bot application needs 2FA too. Adding the bot to your server covers the invite.
  • Stripe: create the product and recurring price, add a webhook endpoint subscribed to checkout.session.completed, customer.subscription.updated and customer.subscription.deleted, set the final retry outcome to cancel or unpaid, and turn on the customer portal so members can cancel without messaging you.
  • Hosting: the webhook has to be reachable over HTTPS at all times. Hosting a Discord bot 24/7 compares the options.

When to hand it off

If one role on one Stripe price is all you need, the code above plus a nightly job gets you there. If you need tiers, a migration from Patreon or LaunchPass without members resubscribing, trials, or a dashboard, I build and run exactly this as a paid membership bot on your own Stripe account and Discord application. Send a brief and I'll reply with a fixed quote.

Questions

Can I use Stripe on Discord?

Yes, but not inside Discord's own checkout. The member pays on a Stripe Checkout page, and a bot with the Manage Roles permission gives them a role when Stripe's webhook confirms the payment. Hosted tools such as LaunchPass and Upgrade.chat do the same thing on top of your Stripe account.

Does Discord take a cut when I sell roles through my own Stripe account?

Discord takes a cut only when the sale goes through Discord: on desktop and web, 10% on Server Subscriptions and 15% (later 30%) on Premium Apps, after a 6% processing fee. A Stripe payment on your own site pays Stripe's fees, not Discord's. Check the monetization section of Discord's Developer Policy before you launch.

Is there a free bot that gives a role after a Stripe payment?

Androz2091/stripe-discord-bot is free: members type their Stripe email with /email, and a daily check removes lapsed roles. You host it yourself with PostgreSQL. The repository had no license file when I checked in September 2026, so ask the author before running a modified copy commercially.

Should I kick members who stop paying or only remove the role?

Remove the role. The member keeps their history and can resubscribe without a new invite, and your member count doesn't swing every month. LaunchPass kicks canceled members by default, and its dontKickOnCancel command switches it to role removal.

What should happen when a member files a chargeback?

Stripe charges $15 per dispute in the US, plus $15 if you counter it (refunded if you win). If you want access to end when a dispute opens, your own bot has to listen for Stripe's charge.dispute.created event and remove the role for that customer.

Sources

Prices and features were checked on September 27, 2026.

  1. Stripe, Pricing (US): cards, disputes, Billing, Tax
  2. Stripe docs, How subscriptions work (statuses)
  3. Stripe docs, Automate payment retries (Smart Retries)
  4. Stripe docs, Receive Stripe events in your webhook endpoint (signatures, ordering, retries)
  5. Stripe docs, Using webhooks with subscriptions
  6. Stripe docs, Create a Checkout Session (client_reference_id, subscription_data)
  7. Stripe docs, Test card numbers
  8. Stripe docs, Simulations and test clocks
  9. Stripe CLI fixture for customer.subscription.deleted (no metadata)
  10. LaunchPass Help Center, How much does LaunchPass cost? (updated Oct 21, 2025)
  11. LaunchPass Help Center, Select your LaunchPass plan (Free plan cannot charge)
  12. LaunchPass Help Center, Can you remove roles instead of kicking members?
  13. Whop docs, Fees
  14. Upgrade.chat, Pricing (monthly and lifetime plans)
  15. Upgrade.chat docs, Payment processors (your own PayPal and Stripe accounts)
  16. Patreon Help Center, Creator fees overview
  17. Patreon Help Center, Setting up Discord for your members
  18. Discord Creator Support, Server Shop for server owners and admins
  19. Discord Creator Support, Creator Revenue FAQ
  20. Discord Support, Server Subscriptions for members (iOS and Android)
  21. Discord, Monetization Terms (Schedule 1, fees)
  22. Discord Developer Support, What are Premium Apps?
  23. Discord Developer Support, Premium Apps Payout
  24. Discord Developer Support, Premium Apps' required support for monetizing apps
  25. Discord Developer Policy (Monetization Requirements)
  26. Discord Developer Support, Changes to Privileged Intent Access for Discord Apps
  27. Discord developer docs, Guild resource (member role endpoints, Add Guild Member)
  28. Discord developer docs, Permissions (hierarchy, 2FA requirement)
  29. Discord developer docs, OAuth2 (identify and guilds.join scopes)
  30. Discord developer docs, JSON error codes (10007 Unknown member)
  31. GitHub, Androz2091/stripe-discord-bot
  32. PebbleHost, Discord bot hosting
  33. Railway, Pricing
  34. Railway docs, Working with domains (Railway-provided domains, SSL)

Tell me what your server needs

Send a short brief. You get a reply within one business day with questions or a price range, and a fixed quote before any work starts.