A new way to donate

On roblox, there are many ways you can set up a way for someone to donate to you. We’ve had “support me” t-shirts for over a decade now. As roblox evolved, we haven’t really gotten better ways. There’s always these one time purchases of a specific amount.

Donations work much better, and reach much more people when a person can choose the amount their donating. Where a 50 R$ shirt might attract 10 people off, a 10 R$ shirt might reach 100. These numbers are just examples and don’t have any statistics behind them other than the intuition that more people can afford to purchase something when it costs less potentially leading to more revenue in the long term.

Almost 2 years ago now, I released a system that automatically created developer products for you, kept track of them, and allowed people to donate any amount. You can find that here.

This system comes with multiple major downsides. First, it requires a vps of some sort with which to run a small node.js server. Second, it requires your roblox cookie to access the api. This is a security vulnerability, but at the time there was no alternative. Third, it could randomly go down and detract people from donating.

This new system is a single roblox script, allows for donating any amount, uses no http requests, and makes it as easy to use as DonationService:Donate(player, amount), which is usable on both the server and the client. If you use this from the client, player is not necessary.

So without further ado, here’s the script, this goes in a module script.:

local MarketplaceService = game:GetService("MarketplaceService")
local RunService = game:GetService("RunService")

local ExecuteDonation = script:WaitForChild("ExecuteDonation")

local DonationService = {
	Products = {}
}

local Players = {}

function DonationService:GetNeededProducts(amount)
	local amountLeft = amount
	local products = {}
	
	while amountLeft > 0 do
		for _, product in ipairs(DonationService.Products) do
			if amountLeft >= product.Robux then
				table.insert(products, {Robux=product.Robux;Id=product.Id;})
				amountLeft -= product.Robux
				break
			end
		end
	end
	
	return products
end

function DonationService:Donate(player, amount)
	if typeof(player) == "number" then
		amount = player
		player = nil
	end
	
	if amount == nil then return end

	if RunService:IsClient() then
		ExecuteDonation:FireServer(amount)
	else
		DonationService:ExecuteDonation(player, amount)
	end
end

function DonationService:ExecuteDonation(player, amount)
	if amount == nil or RunService:IsClient() then return end

	local neededProducts = DonationService:GetNeededProducts(amount)

	for _, product in ipairs(neededProducts) do
		DonationService:Buy(player, product.Id)
		task.wait()
		if not Players[player][3] then break end
	end
	
	Players[player] = nil
end

function DonationService:Buy(player, id)
	if RunService:IsClient() then return end
	MarketplaceService:PromptProductPurchase(player, id)
	Players[player] = {MarketplaceService.PromptProductPurchaseFinished:Wait()}
end

if RunService:IsServer() then
	--[[
		IMPORTANT: IF YOU USE THIS ANYWHERE ELSE, REMOVE IT FROM HERE. YOU CAN ONLY HAVE ONE CALLBACK FUNCTION, 
		AND THAT SHOULD HANDLE YOUR DONATIONS AS WELL AS ANY OTHER PURCHASES
	]]
	MarketplaceService.ProcessReceipt = function() 
		return Enum.ProductPurchaseDecision.PurchaseGranted
	end

	ExecuteDonation.OnServerEvent:Connect(function(player, amount)
		DonationService:ExecuteDonation(player, amount)
	end)
end

return DonationService

This system requires a small amount configuration. First, you’ll need to create multiple developer products for the system to use to complete the donation. The only required product is one that costs 1 R$. Though I recommend setting up products for (at least) 1, 5, 10, 20, 50, 100, 500, and 1000.

Like so:

Once you make these products, you need to add them to the products table.

It is imperative that you add them in descending robux order. If you set them up any other way, this system will fail. Here’s an example of what it should look like in the end:

local DonationService = {
	Products = {
		{ 
			Robux=1000;
			Id=id;
		},
		{ 
			Robux=500;
			Id=id;
		},
		{ 
			Robux=100;
			Id=id;
		},
		{ 
			Robux=50;
			Id=id;
		},
		{ 
			Robux=20;
			Id=id;
		},
		{ 
			Robux=10;
			Id=id;
		},
		{ 
			Robux=5;
			Id=id;
		},
		{ 
			Robux=1;
			Id=id;
		}
	}
}

Where Id is the id of the product that corresponds to that value.

If you set this all up correctly, that’s it. You can make any products you want to make the experience require as few steps as possible, though as stated previously, you will need a product with a value of 1 R$.

After you do this, you can make any kind of UI you want, here’s an example:

As you can see, when a player cancels one, the entire chain is stopped to make the experience as smooth as possible. You don’t need a place dedicated to donations either, you can put this in your actual game somewhere where players can tip you, almost like a built in ko-fi in your game. Basically, this script creates a literal tip jar for your game.

Important note: If you plan to use this from the client, it must be required from the server somewhere at least once. Your structure must look like this or it will fail to load:
3j5xhj_163021

Regardless of whether you use it from the client or server, the remote event ExecuteDonation must be parented to the DonationService ModuleScript.

The advantages of this script include:

  • Not limited to one time purchases, like shirts, game passes, etc.
  • Allows a user to donate a custom amount
  • No third party requests
  • Minimal configuration
  • Extremely simple to use from either the server or the client

If you want to check out the place in the above video, you can do so here, I’ve also uncopylocked it so you can copy it if you so choose, just remember to set up the products to ones you make:

You can even leave a tip if you want, but don’t feel obligated to do so.

Hopefully this script can help some people out there that are looking to mimic what it does.

Another potential use case in the commission world. Your client could just load up the game, type in the value and pay you directly with no group payout 2 week wait time, etc., required at all.

16 Likes

I’ve had an idea like that in my head for years. Even though it would be better if you was creating products live.

1 Like

To my knowledge there’s no way to create dev products from roblox itself in a secure way. That’s what I made my previous service for that was mentioned in the original post.

That was here:

I went over it’s flaws in the main post though and I personally wouldn’t recommend it.

1 Like

this is a really interesting way to tackle this, I feel like it is certainly a smart

i had a (somewhat) similar idea a while ago

this was more of a proof-of-concept more than anything, but basically I made a python script to mass create developer products (over the span of a few hours because of ratelimits) and then used Roblox API to put all these products in to a JSON format and decoded in to a lua table, in which I made this game allowing for any value to be donated (max 1000)

pretty cool concept

Yeah I thought about doing something like that. A major pro of your system is the one click system. That way a donation could be done with one purchase instead of multiple. A drawback I can think of comes with the bloating of the devproduct list. Unfortunately roblox’s UI for the dev product management is terribly outdated and extremely simplistic. However, I suppose you don’t actually need to care about it that much if you’re never gonna look at the menu again. Each system comes with its own pros and cons, and ultimately it’d be best up to the developer to decide which one makes the most sense for their purpose.

1 Like

I had so many ideas about this before, and I never knew how to go about it. This is awesome, and I think the way you went about it is really clever. Good job

2 Likes

I know there is a way (already saw that in a game but that was private…) it worked with HttpService and the roblox games api (and a proxy bcs http service don’t allow request to roblox)