
You want members to buy roles in your server: a colored name, access to a private channel, a VIP badge. Discord gives you two very different ways to do that. Members can pay with coins they earn from a bot, or they can pay real money through Discord's own Server Shop and Server Subscriptions. The second route has strict eligibility rules that rule out every server owner outside the United States, so check them before you plan around it.
Quick answer: A Discord role shop system needs three parts: a currency, a list of roles with prices, and something with permission to hand out the roles. For earned coins, use an economy bot such as UnbelievaBoat (!create-item with a role attached, members run !buy-item) or run your own bot with a small database. For real money, Discord's Server Shop sells one-time Premium Roles and Server Subscriptions sell monthly tier roles, but on 3 November 2026 both require a server owner aged 18 or older with 2FA, a Community server, and a US bank account and US ID. Discord keeps 10% of each sale, and processing fees come off on top.
Three ways to build a Discord role shop system
| Option | Members pay with | Who can set it up | Recurring | Where the data lives |
|---|---|---|---|---|
| Economy bot store (UnbelievaBoat and similar) | Coins earned in the server | Any server admin | No | The bot company's servers |
| Discord Server Shop (Premium Roles) | Real money, one-time | US-based owners; still in beta for a select group of servers | No | Discord |
| Discord Server Subscriptions | Real money, monthly | US-based owners who meet Discord's rules | Yes | Discord |
| Your own bot | Coins (or anything you build) | Anyone who can run a bot | Your choice | Your own database |
Keep earned currency and real money apart. A coin shop is a game inside your server. Selling roles for money brings in payments, refunds and Discord's Monetization Policy, so use Discord's own tools for that or an outside platform with its own Discord integration.
How a user role shop works with bot currency
Every bot shop runs the same loop:
- Members earn coins by chatting, claiming a daily reward or using work commands.
- The bot stores each member's balance.
- Members list the shop and buy a role.
- The bot takes the coins and adds the role to the member.
Step 4 is the one that needs setup. Discord's permission rules say "a bot can grant roles to other users that are of a lower position than its own highest role." So the bot needs the Manage Roles permission, and its own role must sit above every role you sell in Server Settings > Roles. If it does not, Discord answers with error 50013, "You lack permissions to perform that action." Our 50013 fix guide walks through the role order.
Leveling bots that give a role at a set level are a different system: members cannot choose or spend anything. If that is what you want, see our leveling bots comparison.
Set up a role shop with UnbelievaBoat
UnbelievaBoat has a store, items, inventories and a cash and bank balance. The commands below use the ! prefix shown in its command list.
Let members earn coins from chat in every channel, 10 to 25 coins per message, at most once per 30 seconds:
!chat-money-channels enable all
!chat-money-amount min 10
!chat-money-amount max 25
!chat-money-cooldown 30 seconds
Create a role item:
!create-item
Run it without a name and the bot guides you through every option; type cancel to stop. Items have options for price, stock, role-given, role-required, role-removed, required-balance and inventory. Set role-given to the role you are selling. You can change any option later with !edit-item <option> <item name> [new value], for example !edit-item price "Red Name" 500, and !item-options explains each one.
What members use:
!money shows your balance and leaderboard position
!store lists the items for sale
!buy-item Red Name buys one Red Name
!inventory shows what you own
!use-item Red Name uses an item; if it comes with a role, you get the role
Two options are handy for roles. role-required limits an item to members who already hold a given role, which lets you build a ladder of Bronze, Silver and Gold, and stock caps how many can be sold, which suits a limited event role. !reset-economy sets everyone's balance back to 0 if inflation gets out of hand.
Discord's own paid roles: Server Shop and Server Subscriptions
Discord sells roles for real money in two ways, both set up from your server settings after the owner enables monetization.
Server Subscriptions are monthly. You create 1 to 3 paid tiers, and each tier comes with a role you can use to gate channels, or you can make the whole server paid. You can add up to 25 premium emoji for subscribers and offer free trials of one week or one day.
Server Shop sells Server Products as one-time purchases. A Premium Role is "a role with special permissions" that gives access to a channel or other perk "without requiring a monthly subscription", such as a ticket to a stage event. Downloadables (files) sit in the same shop. Once you publish a product, Server Shop appears at the top of your channel list. Discord's server owner guide, as it read on 3 November 2026, says Server Products are "currently in beta testing and available to a select group of servers", so do not count on having it.
Eligibility rules for Server Shop and Server Subscriptions
From Discord's creator support pages and Monetization Policy, opened on 3 November 2026:
- You must be the server owner and 18 years or older.
- Your account must be in good standing: no safety violation in the past 30 days, no fraud flags, no unpaid charges to Discord.
- Your email must be verified (the Creator Revenue FAQ also asks for a verified phone number), and Two-Factor Authentication must be on.
- You must be based in the United States and give Stripe a US bank account and US identification. The FAQ says Server Subscriptions "will not be available outside of the United States."
- The server needs Community enabled, MFA required for moderation actions, and must not be built mainly around NSFW content.
- Most of the server's members and activity must not be bots or inactive accounts, and the server must not have changed owner in the past 30 days without Discord's approval.
- Nothing you sell may be on Stripe's prohibited and restricted businesses list or in Discord's list of content you may not monetize.
That last list matters for economy servers. Discord's Monetization Policy names "simulated casinos and their games" under gambling-adjacent content you may not monetize. UnbelievaBoat ships blackjack, roulette and slot machine commands, so keep them away from anything you sell, or switch them off with the bot's disable command in a monetized server.
Fees and payouts
Discord uses a 90/10 split: you keep 90% of each subscription or one-time purchase and Discord keeps 10%, and payment processing and other transaction fees come off on top. Subscriptions started in the iOS app cost the member more, because Apple takes an extra 30%, and Server Subscriptions cannot be bought in the Android app. Your first payout needs at least $100 earned, later payouts $25, and Discord processes payouts once a month, on or before the 15th. Premium Roles and other Server Products cannot be refunded, so describe them honestly.
To start, the owner opens the Developer Portal, picks the server under Servers, chooses Enable Monetization, accepts the Monetization Terms and then sets up a payout team with Stripe.
Run your own role shop bot
If you want full control over prices, earning rules and data, a self-hosted bot fits in one file of about 110 lines. This one uses discord.js 14.28.0 (released 30 October 2026) and better-sqlite3 13, which needs Node.js 22 or newer. On Node.js 20, install better-sqlite3@12 instead; the code is the same for both. It handles one server: /daily gives 100 coins once per 24 hours, /balance and /shop show coins and prices, /buy sells a role, and /additem (by default only for members with Manage Roles) puts a role on sale.
Set up the project:
mkdir role-shop && cd role-shop
npm init -y
npm install discord.js better-sqlite3
Save this as shop.js:
// Role shop for one server: discord.js 14 + better-sqlite3
const {
Client, Events, GatewayIntentBits, MessageFlags,
PermissionFlagsBits, SlashCommandBuilder,
} = require('discord.js');
const Database = require('better-sqlite3');
const DAILY_COINS = 100;
const DAY_MS = 24 * 60 * 60 * 1000;
const db = new Database('shop.db');
db.pragma('journal_mode = WAL');
db.exec(`
CREATE TABLE IF NOT EXISTS wallets (
user_id TEXT PRIMARY KEY,
coins INTEGER NOT NULL DEFAULT 0,
last_daily INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS items (role_id TEXT PRIMARY KEY, price INTEGER NOT NULL);
CREATE TABLE IF NOT EXISTS purchases (user_id TEXT, role_id TEXT, price INTEGER, bought_at INTEGER);
`);
const sql = {
ensure: db.prepare('INSERT OR IGNORE INTO wallets (user_id) VALUES (?)'),
wallet: db.prepare('SELECT coins FROM wallets WHERE user_id = ?'),
daily: db.prepare('UPDATE wallets SET coins = coins + ?, last_daily = ? WHERE user_id = ? AND last_daily <= ?'),
spend: db.prepare('UPDATE wallets SET coins = coins - ? WHERE user_id = ? AND coins >= ?'),
refund: db.prepare('UPDATE wallets SET coins = coins + ? WHERE user_id = ?'),
item: db.prepare('SELECT price FROM items WHERE role_id = ?'),
items: db.prepare('SELECT role_id, price FROM items ORDER BY price'),
setItem: db.prepare('INSERT INTO items (role_id, price) VALUES (?, ?) ON CONFLICT(role_id) DO UPDATE SET price = excluded.price'),
log: db.prepare('INSERT INTO purchases VALUES (?, ?, ?, ?)'),
};
const commands = [
new SlashCommandBuilder().setName('daily').setDescription(`Collect ${DAILY_COINS} coins once a day`),
new SlashCommandBuilder().setName('balance').setDescription('Show your coins'),
new SlashCommandBuilder().setName('shop').setDescription('List the roles for sale'),
new SlashCommandBuilder().setName('buy').setDescription('Buy a role from the shop')
.addRoleOption((o) => o.setName('role').setDescription('Role to buy').setRequired(true)),
new SlashCommandBuilder().setName('additem').setDescription('Put a role in the shop or change its price')
.setDefaultMemberPermissions(PermissionFlagsBits.ManageRoles)
.addRoleOption((o) => o.setName('role').setDescription('Role to sell').setRequired(true))
.addIntegerOption((o) => o.setName('price').setDescription('Price in coins').setRequired(true).setMinValue(1)),
].map((c) => c.toJSON());
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once(Events.ClientReady, async (ready) => {
const guild = await ready.guilds.fetch(process.env.GUILD_ID);
await guild.commands.set(commands);
console.log(`Role shop online as ${ready.user.tag}`);
});
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild()) return;
const reply = (content) =>
interaction.reply({ content, flags: MessageFlags.Ephemeral, allowedMentions: { parse: [] } });
const userId = interaction.user.id;
sql.ensure.run(userId);
if (interaction.commandName === 'daily') {
const now = Date.now();
const { changes } = sql.daily.run(DAILY_COINS, now, userId, now - DAY_MS);
if (changes === 0) return reply('You already collected your coins. Try again later.');
return reply(`+${DAILY_COINS} coins. You now have ${sql.wallet.get(userId).coins}.`);
}
if (interaction.commandName === 'balance') {
return reply(`You have ${sql.wallet.get(userId).coins} coins.`);
}
if (interaction.commandName === 'shop') {
const rows = sql.items.all();
if (rows.length === 0) return reply('The shop is empty.');
return reply(rows.map((r) => `<@&${r.role_id}>: ${r.price} coins`).join('\n'));
}
if (interaction.commandName === 'additem') {
const role = interaction.options.getRole('role', true);
const price = interaction.options.getInteger('price', true);
if (!role.editable) return reply(`I cannot give ${role.name}. Move my role above it first.`);
sql.setItem.run(role.id, price);
return reply(`${role.name} now costs ${price} coins.`);
}
if (interaction.commandName === 'buy') {
const role = interaction.options.getRole('role', true);
const item = sql.item.get(role.id);
if (!item) return reply(`${role.name} is not for sale.`);
if (interaction.member.roles.cache.has(role.id)) return reply('You already have that role.');
if (!role.editable) return reply('I cannot give that role right now. Ask an admin to check my role position.');
if (sql.spend.run(item.price, userId, item.price).changes === 0) {
return reply(`${role.name} costs ${item.price} coins. Check /balance.`);
}
try {
await interaction.member.roles.add(role, 'Role shop purchase');
} catch (err) {
sql.refund.run(item.price, userId);
console.error(err);
return reply('Discord refused the role change. Your coins are back.');
}
sql.log.run(userId, role.id, item.price, Date.now());
return reply(`You bought ${role.name} for ${item.price} coins.`);
}
});
client.login(process.env.DISCORD_TOKEN);
Start it with your bot token and server ID:
DISCORD_TOKEN=your-bot-token GUILD_ID=your-server-id node shop.js
A few details that make it safe to run:
- The
spendquery only takes coins whencoins >= price, in one statement, so two fast/buyclicks cannot push a balance below zero. - If Discord refuses the role change, the coins go back and nothing is logged.
role.editablechecks Manage Roles and the role order before any coins move. - Invite the bot with the
botandapplications.commandsscopes and the Manage Roles permission, then drag its role above the roles you sell. - Every purchase lands in the
purchasestable, so you can answer "I paid and got nothing" with a query instead of a guess. - Keep the token in an environment variable, never in the file. Our guide on token security explains why.
A self-hosted shop only works while the process runs and shop.db stays on disk, so a laptop that goes to sleep closes the shop. On our Discord bot hosting, Pro (1 GB RAM, 20 GB NVMe) is the smallest plan listed as database ready, with room for a SQLite file like this one. Either way, back up shop.db regularly with better-sqlite3's db.backup() method, or copy the file while the bot is stopped. For bigger setups, our database guide covers moving to MySQL.
Keep the economy healthy
- Price the first role so an active member can afford it within a week, and put the best roles well above that.
- Sell cosmetic roles (colors, badges, profile flair) rather than roles with moderation power, so the shop does not become pay to win.
- Add coin sinks such as limited event roles (UnbelievaBoat's
stockoption) so balances do not grow forever. - Log purchases and post the price list in a read-only channel so nobody argues about what something cost.
- Reset or rebalance when coins stop meaning anything, and announce it first.
FAQ
What is a Discord role shop system? A setup where members spend a currency on roles. Usually a bot tracks coins that members earn by chatting or daily rewards, and adds the role when they buy it. Discord's own Server Shop and Server Subscriptions are the real-money versions.
Can members buy roles with real money on Discord? Yes, through Server Subscriptions (monthly tier roles) or Premium Roles in the Server Shop (one-time). Both need a server owner who meets Discord's eligibility rules, including a US bank account and US ID, and Server Products are still in beta for a select group of servers.
Can I sell roles if I live outside the US? Not through Discord on 3 November 2026. The Creator Revenue FAQ says Server Subscriptions will not be available outside the United States, and Stripe onboarding only accepts US banking and ID. An outside membership platform can work instead: Patreon, for example, gives patrons a role in the creator's server once they link their Discord account. See our Discord server monetization guide.
Why does my role shop bot not give the role? Almost always the role order. The bot needs Manage Roles, and its highest role must be above the role it sells. Roles managed by an integration, such as another bot's own role, cannot be handed out at all; discord.js reports them as not editable.
Is a user role shop free? A coin shop is free to run with a public economy bot. A self-hosted bot costs whatever its hosting costs. With Discord's paid options, Discord keeps 10% of each sale plus fees.
Which bot is best for a role shop? For a ready-made store with items, stock limits and role requirements, UnbelievaBoat covers the usual needs without code. If you want your own earning rules or your own data, write a small bot like the one above. Our economy bot guide shows a bigger build with games and an item shop.
Sources
All opened on 3 November 2026:
- Discord Creator Support: Server Shop For Server Owners and Admins, Creator Revenue FAQ, Server Subs: Payout Onboarding Guide
- Discord Support: Server Subscriptions for Members, Monetization Policy, Monetization Terms
- Discord Developer Docs: Permissions and permission hierarchy, JSON error codes
- Stripe prohibited and restricted businesses
- UnbelievaBoat command list
- discord.js on npm (14.28.0), discord.js documentation and Role.js source
- better-sqlite3 API documentation and better-sqlite3 on npm
- Patreon: Getting Discord access
Related: Discord reaction roles setup, Best VPS for Discord bots, Discord bot hosting guide