Canceling ProcessReceipt (PolicyService)

Since there is not a lot of documentation for PolicyService, I am having a bit of trouble right now.

If I have a “loot box” type dev product, of course I can add code to the button so the player can only buy the product if ArePaidRandomItemsRestricted is false.

The issue is, if the player uses exploits to still prompt the purchase from the client, is there a way to cancel the purchase in the ProcessReceipt function?

I tried removing return Enum.ProductPurchaseDecision.PurchaseGranted but the purchase still somehow went through. And return Enum.ProductPurchaseDecision.NotYetProcessed would make the ProcessReceipt function fire again when there is a chance. I dont want to charge the player if they arent even allowed to get the rewards due to policy service.

Server code:

local mps = game:GetService("MarketplaceService")
local ps = game:GetService("PolicyService")

local lootBoxId = 0 -- the id

--// PRODUCT BOUGHT
mps.ProcessReceipt = function(info)
	local plr = game.Players:GetPlayerByUserId(info.PlayerId)

	--// RANDOM UNLOCK
	if info.ProductId == lootBoxId then
		local policy = ps:GetPolicyInfoForPlayerAsync(plr)

		if policy.ArePaidRandomItemsRestricted == false then
			-- add random item here
		else
			-- what do i do here?
		end
	end


	--// GRANT PURCHASE
	return Enum.ProductPurchaseDecision.PurchaseGranted
end

Button code:

local plr = game.Players.LocalPlayer
local ms = game:GetService("MarketplaceService")
local ps = game:GetService("PolicyService")

script.Parent.Visible = false

task.wait(2)
local info = ps:GetPolicyInfoForPlayerAsync(plr)

if info.ArePaidRandomItemsRestricted == false then
	script.Parent.Visible = true
end

script.Parent.MouseButton1Click:Connect(function()
	if info.ArePaidRandomItemsRestricted == false then
		ms:PromptProductPurchase(plr, currentId)
	else
		
	end
end)

What can I do about this?

Since the player’s policy info can be checked locally, couldn’t you just prevent the UI from being accessed to those who can’t purchase random items?

Seems like you already did it here:

Don’t prompt the purchase if ArePaidRandomItemsRestriction is true.

Read my post again:

If an exploiter does this, there is no way for me to cancel the purchase.

Though I dont know the rules for the policy service considering exploits. Would you still get in trouble if policy service got bypassed by using an exploit? Should you just not give anything to the player in that situation, but still mark the purchase as PurchaseGranted?

Thats why this is frustrating to me. This is rather important, but Roblox has not explained it well at all. We were given almost no policy service code samples (neither in the PolicyService dev hub page, or the ProcessReceipt dev hub page, though we were given a short code sample of how to get the policies in the first place, which is not helpful in this case)

1 Like

Returning ProductPurchaseDecision.NotProcessedYet would eventually stop it from retrying, but when it wants to try again at a later time (such as them rejoining the game), it’ll try again. The only real safe solution would likely be to return ProductPurchaseDecision.PurchasedGranted.

While nothing was actually rewarded to the player, Roblox currently doesn’t give you any way to actually permanently cancel the product purchase, aside from PurchasedGranted.

I don’t believe you’d ever be held responsible for something like this. As long as your game won’t offer these mechanics for users where it’s banned in their territory per policy, you should be fine. If a player willingly exploits your game to try and bypass this, I don’t believe you’re realistically expected to make sure they don’t. Of course, since Roblox hasn’t actually stated this, I couldn’t absolutely confirm/deny this as it’s just my best guess at this point.

I’d likely assume the statement above, but try to make your code block off any malicious actors regardless, just to be safe.

1 Like

If an exploiter were to force this prompt themselves, thats on them. You would not be held responsible for the breach of policy because its not an intended function of the game for that user.

2 Likes