I am following my usual knowledge source of the Roblox Education site for learning how to make In-Game Purchases. The article in question is here:
However whether I make a test purchase in Studio I don’t seem to capture a success return from MarketplaceService.ProcessReceipt.
Thinking this was because I was in Studio, I made the purchases live in-game and it still failed to return a success when I tested.
I know the client side GUI portion works OK, as the live in-game purchase was deducted from my Robux total.
Can anybody see where I am going wrong:
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
-- Data store for tracking purchases that were successfully processed
local purchaseHistoryStore = DataStoreService:GetOrderedDataStore("DonationHistory")
-- Table setup containing product IDs and functions for handling purchases
local productFunctions = {}
-- ProductId 111111111111
productFunctions[111111111111] = function(receipt, player)
-- Logic/code for Thanking Player
print(player.Name .. " bought 111111111111")
end
-- ProductId 22222222222
productFunctions[22222222222] = function(receipt, player)
-- Logic/code for Thanking Player
print(player.Name .. " bought 22222222222")
end
local function processReceipt(receiptInfo)
-- Determine if the product was already granted by checking the data store
local playerProductKey = receiptInfo.PlayerId .. "_" .. receiptInfo.PurchaseId
local purchased = false
local success, errorMessage = pcall(function()
purchased = purchaseHistoryStore:GetAsync(playerProductKey)
end)
-- If purchase was recorded, the product was already granted
if success and purchased then
return Enum.ProductPurchaseDecision.PurchaseGranted
elseif not success then
error("Data store error:" .. errorMessage)
end
-- Find the player who made the purchase in the server
local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
if not player then
-- The player probably left the game
-- If they come back, the callback will be called again
return Enum.ProductPurchaseDecision.NotProcessedYet
end
-- Look up handler function from 'productFunctions' table above
local handler = productFunctions[receiptInfo.ProductId]
-- Call the handler function and catch any errors
local success, result = pcall(handler, receiptInfo, player)
if not success or not result then
warn("Error occurred while processing a product purchase")
print("ProductId:", receiptInfo.ProductId)
print("Player:", player)
return Enum.ProductPurchaseDecision.NotProcessedYet
end
-- Record transaction in data store so it isn't granted again
local success, errorMessage = pcall(function()
purchaseHistoryStore:SetAsync(playerProductKey, true)
end)
if not success then
error("Cannot save purchase data: " .. errorMessage)
end
-- IMPORTANT: Tell Roblox that the game successfully handled the purchase
return Enum.ProductPurchaseDecision.PurchaseGranted
end
MarketplaceService.ProcessReceipt = processReceipt
I am keen to get this to work as Premium Payouts are never going to earn me a cup of coffee each month.
Have you tried putting a print statement at the top of the processReceipt function to check if it runs at all?
Also have you tried using tonumber on the ProductId before indexing productFunctions (and/or making the keys strings)? It may be that you get a string value back from MarketplaceService instead of an integer.
So adding the following at the top of the function:
print (receiptInfo)
for i, v in pairs(receiptInfo) do
print ("I: " .. i)
print ("V type: " .. typeof(v))
print ("V: " .. v)
end
Gives a nice little table of the returned info:
table: 0x7a4a39908d4affc8
I: CurrencySpent
V type: number
V: 0
I: PurchaseId
V type: string
V: 2046550ca74e755c65353d4434e25915
I: ProductId
V type: number
V: 1104844206
I: PlaceIdWherePurchased
V type: number
V: 5271743600
I: CurrencyType
V type: EnumItem
So both ProductId and PlaceIdWherePurchased are integers
Okay good, so it’s running and returning the right things.
You don’t have a productFunction at the index 1104844206 so there’s nothing to run. Your pcall therefore can’t return success, so you should see the warning message.
You only have productFunctions at 111111111111 and 22222222222.
That was just bogus numbers I put in for posting here initially. The actual functions do exist with real ProductIDs which I have verified:
productFunctions[1104844206] = function(receipt, player)
-- Logic/code for Thanking Player
print(player.Name .. " Donated 25 RBXP")
end
productFunctions[1104848933] = function(receipt, player)
-- Logic/code for Thanking Player
print(player.Name .. " Donated 50 RBXP")
end
productFunctions[1104849360] = function(receipt, player)
-- Logic/code for Thanking Player
print(player.Name .. " Donated 100 RBXP")
end
So for the table output above, it returned the first ProductID which is specified above.
It is actually failing at the following point:
local success, result = pcall(handler, receiptInfo, player)
if not success or not result then
warn("Error occurred while processing a product purchase")
print("ProductId:", receiptInfo.ProductId)
print("Player:", player)
return Enum.ProductPurchaseDecision.NotProcessedYet
end
All of the warnings print fine in console. Adding the print(success, result) outputs:
true nil
01:18:23.078 - Error occurred while processing a product purchase
ProductId: 1104848933
Player: EmilyGaming9081
That to me implies that it can’t find the matching productFunctions[1104848933] earlier in the script, which doesn’t make sense to me at all.
^^ I used a different productID this time round, just to be really helpful
Thank you @BanTech. Took me longer than expected to get around to testing as work interfered with life but this but it finally works and resultant returns are now saved into a Datastore.