2013/luau/107893730.luau

1393 lines
39 KiB
Plaintext

-- CoreGui.RobloxGui.CoreScripts/PurchasePromptScript
print "[Mercury]: Loaded corescript 107893730"
-- this script creates the gui and sends the web requests for in game purchase prompts
local MarketplaceService = game:GetService "MarketplaceService"
local GuiService = game:GetService "GuiService"
-- wait for important items to appear
while not Game do
wait(0.1)
end
while not MarketplaceService do
wait(0.1)
MarketplaceService = game:GetService "MarketplaceService"
end
while not game:FindFirstChild "CoreGui" do
wait(0.1)
end
while not game.CoreGui:FindFirstChild "RobloxGui" do
wait(0.1)
end
-------------------------------- Global Variables ----------------------------------------
-- utility variables
local RbxUtility
local baseUrl = game:GetService("ContentProvider").BaseUrl:lower()
-- data variables
local currentProductInfo, currentAssetId, currentCurrencyType, currentCurrencyAmount, currentEquipOnPurchase, currentProductId, currentServerResponseTable
local checkingPlayerFunds = false
local openBCUpSellWindowConnection
local purchasingConsumable = false
local enableBrowserWindowClosedEvent = true
-- gui variables
local openBuyCurrencyWindowConnection
local currentlyPrompting = false
local purchaseDialog
local tweenTime = 0.3
local showPosition = UDim2.new(0.5, -330, 0.5, -200)
local hidePosition = UDim2.new(0.5, -330, 1, 25)
local isSmallScreen
local spinning = false
local spinnerIcons
local smallScreenThreshold = 450
-- user facing images
local function assetUrl(id: number)
return `https://banland.xyz/asset?id={id}`
end
local errorImageUrl = assetUrl(42557901)
local buyImageUrl = assetUrl(104651457)
local buyImageDownUrl = assetUrl(104651515)
local buyImageDisabledUrl = assetUrl(104651532)
local cancelButtonImageUrl = assetUrl(104651592)
local cancelButtonDownUrl = assetUrl(104651639)
local okButtonUrl = assetUrl(104651665)
local okButtonPressedrl = assetUrl(104651707)
local freeButtonImageUrl = assetUrl(104651733)
local freeButtonImageDownUrl = assetUrl(104651761)
local tixIcon = assetUrl(102481431)
local robuxIcon = assetUrl(102481419)
-- user facing string
local buyHeaderText = "Buy"
local takeHeaderText = "Take"
local buyFailedHeaderText = "An Error Occurred"
local errorPurchasesDisabledText = "in-game purchases are disabled"
local errorPurchasesUnknownText = "Roblox is performing maintenance"
local purchaseSucceededText = "Your purchase of itemName succeeded!"
local purchaseFailedText =
"Your purchase of itemName failed because errorReason. Your account has not been charged. Please try again soon."
local productPurchaseText =
"Would you like to buy 'itemName' for currencyType currencyAmount?"
local freeItemPurchaseText =
"Would you like to take the assetType 'itemName' for FREE?"
local freeItemBalanceText =
"Your balance of Robux or Tix will not be affected by this transaction."
-------------------------------- End Global Variables ----------------------------------------
----------------------------- Util Functions ---------------------------------------------
local function getSecureApiBaseUrl()
local secureApiUrl = baseUrl
secureApiUrl = string.gsub(secureApiUrl, "http", "https")
secureApiUrl = string.gsub(secureApiUrl, "www", "api")
return secureApiUrl
end
local function getRbxUtility()
if not RbxUtility then
RbxUtility = LoadLibrary "RbxUtility"
end
return RbxUtility
end
local function preloadAssets()
for _, url in ipairs {
errorImageUrl,
buyImageUrl,
buyImageDownUrl,
buyImageDisabledUrl,
cancelButtonImageUrl,
cancelButtonDownUrl,
okButtonUrl,
okButtonPressedrl,
freeButtonImageUrl,
freeButtonImageDownUrl,
tixIcon,
robuxIcon,
} do
game:GetService("ContentProvider"):Preload(url)
end
end
----------------------------- End Util Functions ---------------------------------------------
-------------------------------- Accept/Decline Functions --------------------------------------
local function removeCurrentPurchaseInfo()
currentAssetId = nil
currentCurrencyType = nil
currentCurrencyAmount = nil
currentEquipOnPurchase = nil
currentProductId = nil
currentProductInfo = nil
currentServerResponseTable = nil
checkingPlayerFunds = false
end
local function hidePurchasing()
purchaseDialog.PurchasingFrame.Visible = false
spinning = false
end
local function closePurchasePrompt()
purchaseDialog:TweenPosition(
hidePosition,
Enum.EasingDirection.Out,
Enum.EasingStyle.Quad,
tweenTime,
true,
function()
game.GuiService:RemoveCenterDialog(purchaseDialog)
hidePurchasing()
purchaseDialog.Visible = false
currentlyPrompting = false
end
)
end
local function signalPromptEnded(isSuccess)
closePurchasePrompt()
if purchasingConsumable then
MarketplaceService:SignalPromptProductPurchaseFinished(
game.Players.LocalPlayer.userId,
currentProductId,
isSuccess
)
else
MarketplaceService:SignalPromptPurchaseFinished(
game.Players.LocalPlayer,
currentAssetId,
isSuccess
)
end
removeCurrentPurchaseInfo()
end
-- convenience method to say exactly what buttons should be visible (all others are not!)
local function setButtonsVisible(...)
local args = { ... }
local argCount = select("#", ...)
local bodyFrameChildren = purchaseDialog.BodyFrame:GetChildren()
for i = 1, #bodyFrameChildren do
if bodyFrameChildren[i]:IsA "GuiButton" then
bodyFrameChildren[i].Visible = false
for j = 1, argCount do
if bodyFrameChildren[i] == args[j] then
bodyFrameChildren[i].Visible = true
break
end
end
end
end
end
local function userPurchaseActionsEnded(isSuccess: boolean)
checkingPlayerFunds = false
if isSuccess then -- show the user we bought the item successfully, when they close this dialog we will call signalPromptEnded
local newPurchasedSucceededText = string.gsub(
purchaseSucceededText,
"itemName",
tostring(currentProductInfo.Name)
)
purchaseDialog.BodyFrame.ItemPreview.ItemDescription.Text =
newPurchasedSucceededText
setButtonsVisible(purchaseDialog.BodyFrame.OkPurchasedButton)
hidePurchasing()
else -- otherwise we didn't purchase, no need to show anything, just signal and close dialog
signalPromptEnded(isSuccess)
end
end
-- enums have no implicit conversion to numbers in lua, has to have a function to do this
local function currencyEnumToInt(currencyEnum: Enum.CurrencyType)
if currencyEnum == Enum.CurrencyType.Tix then
return 2
end
return 1
end
-- oi, this is ugly
local function assetTypeToString(assetType)
if assetType == 1 then
return "Image"
elseif assetType == 2 then
return "T-Shirt"
elseif assetType == 3 then
return "Audio"
elseif assetType == 4 then
return "Mesh"
elseif assetType == 5 then
return "Lua"
elseif assetType == 6 then
return "HTML"
elseif assetType == 7 then
return "Text"
elseif assetType == 8 then
return "Hat"
elseif assetType == 9 then
return "Place"
elseif assetType == 10 then
return "Model"
elseif assetType == 11 then
return "Shirt"
elseif assetType == 12 then
return "Pants"
elseif assetType == 13 then
return "Decal"
elseif assetType == 16 then
return "Avatar"
elseif assetType == 17 then
return "Head"
elseif assetType == 18 then
return "Face"
elseif assetType == 19 then
return "Gear"
elseif assetType == 21 then
return "Badge"
elseif assetType == 22 then
return "Group Emblem"
elseif assetType == 24 then
return "Animation"
elseif assetType == 25 then
return "Arms"
elseif assetType == 26 then
return "Legs"
elseif assetType == 27 then
return "Torso"
elseif assetType == 28 then
return "Right Arm"
elseif assetType == 29 then
return "Left Arm"
elseif assetType == 30 then
return "Left Leg"
elseif assetType == 31 then
return "Right Leg"
elseif assetType == 32 then
return "Package"
elseif assetType == 33 then
return "YouTube Video"
elseif assetType == 34 then
return "Game Pass"
elseif assetType == 0 then
return "Product"
end
return ""
end
local function isFreeItem()
-- if both of these are true, then the item is free, just prompt user if they want to take one
return currentProductInfo
and currentProductInfo.IsForSale == true
and currentProductInfo.IsPublicDomain == true
end
local function setHeaderText(text)
purchaseDialog.TitleLabel.Text = text
purchaseDialog.TitleBackdrop.Text = text
end
local function currencyTypeToString(currencyType)
if currencyType == Enum.CurrencyType.Tix then
return "Tix"
end
return "R$"
end
-- make sure our gui displays the proper purchase data, and set the productid we will try and buy if use specifies a buy action
local function updatePurchasePromptData(_)
local newItemDescription = ""
-- id to use when we request a purchase
if not currentProductId then
currentProductId = currentProductInfo.ProductId
end
if isFreeItem() then
newItemDescription = string.gsub(
freeItemPurchaseText,
"itemName",
tostring(currentProductInfo.Name)
)
newItemDescription = string.gsub(
newItemDescription,
"assetType",
tostring(assetTypeToString(currentProductInfo.AssetTypeId))
)
setHeaderText(takeHeaderText)
else -- otherwise item costs something, so different prompt
newItemDescription = string.gsub(
productPurchaseText,
"itemName",
tostring(currentProductInfo.Name)
)
newItemDescription = string.gsub(
newItemDescription,
"currencyType",
tostring(currencyTypeToString(currentCurrencyType))
)
newItemDescription = string.gsub(
newItemDescription,
"currencyAmount",
tostring(currentCurrencyAmount)
)
setHeaderText(buyHeaderText)
end
purchaseDialog.BodyFrame.ItemPreview.ItemDescription.Text =
newItemDescription
if purchasingConsumable then
purchaseDialog.BodyFrame.ItemPreview.Image =
`{baseUrl}thumbs/asset.ashx?assetid={currentProductInfo.IconImageAssetId}&x=100&y=100&format=png`
else
purchaseDialog.BodyFrame.ItemPreview.Image =
`{baseUrl}thumbs/asset.ashx?assetid={currentAssetId}&x=100&y=100&format=png`
end
end
-- more enum to int fun!
local function membershipTypeToNumber(membership)
if membership == Enum.MembershipType.None then
return 0
elseif membership == Enum.MembershipType.BuildersClub then
return 1
elseif membership == Enum.MembershipType.TurboBuildersClub then
return 2
elseif membership == Enum.MembershipType.OutrageousBuildersClub then
return 3
end
return -1
end
-- should open an external default browser window to this url
local function openBuyCurrencyWindow()
checkingPlayerFunds = true
GuiService:OpenBrowserWindow(`{baseUrl}Upgrades/Robux.aspx`)
end
-- set up the gui text at the bottom of the prompt (alerts user to how much money they will have left, or if they need to buy more to buy the item)
local function updateAfterBalanceText(playerBalance, notRightBc)
if isFreeItem() then
purchaseDialog.BodyFrame.AfterBalanceButton.Text = freeItemBalanceText
return true, false
end
local keyWord
if currentCurrencyType == Enum.CurrencyType.Robux then
keyWord = "robux"
elseif currentCurrencyType == Enum.CurrencyType.Tix then
keyWord = "tickets"
end
if not keyWord then
return false
end
local playerBalanceNumber = tonumber(playerBalance[keyWord])
if not playerBalanceNumber then
return false
end
local afterBalanceNumber = playerBalanceNumber - currentCurrencyAmount
-- check to see if we have enough of the desired currency to allow a purchase, if not we need to prompt user to buy robux
if not notRightBc then
if afterBalanceNumber < 0 and keyWord == "robux" then
if openBuyCurrencyWindowConnection == nil then
openBuyCurrencyWindowConnection =
purchaseDialog.BodyFrame.AfterBalanceButton.MouseButton1Click:connect(
openBuyCurrencyWindow
)
end
purchaseDialog.BodyFrame.AfterBalanceButton.Text =
`You need R$ {-afterBalanceNumber} more to buy this, click here to purchase more.`
return true, true
elseif afterBalanceNumber < 0 and keyWord == "tickets" then
purchaseDialog.BodyFrame.AfterBalanceButton.Text =
`You need {-afterBalanceNumber} Tix more to buy this item.`
return true, true -- user can't buy more tickets, so we say fail the transaction (maybe instead we can prompt them to trade currency???)
end
end
-- this ensures that we only have one connection to openBuyCurrencyWindow at a time (otherwise might open multiple browser windows)
if openBuyCurrencyWindowConnection then
openBuyCurrencyWindowConnection:disconnect()
openBuyCurrencyWindowConnection = nil
end
purchaseDialog.BodyFrame.AfterBalanceButton.Text =
`Your balance after this transaction will be {currencyTypeToString(
currentCurrencyType
)} {afterBalanceNumber}.`
return true, false
end
-- will get the player's balance of robux and tix, return in a table
local function getPlayerBalance()
local playerBalance
local success, errorCode = ypcall(function()
playerBalance =
game:HttpGetAsync(`{getSecureApiBaseUrl()}currency/balance`)
end)
if not success then
print("Get player balance failed because", errorCode)
return
end
if playerBalance == "" then
return
end
playerBalance = getRbxUtility().DecodeJSON(playerBalance)
return playerBalance
end
-- figure out what currency to use based on the currency you can actually sell the item in and what the script specified
local function setCurrencyAmountAndType(priceInRobux, priceInTix)
if
currentCurrencyType == Enum.CurrencyType.Default
or currentCurrencyType == Enum.CurrencyType.Robux
then -- sell for default (user doesn't care) or robux
if priceInRobux ~= nil and priceInRobux ~= 0 then -- we can sell for robux
currentCurrencyAmount = priceInRobux
currentCurrencyType = Enum.CurrencyType.Robux
else -- have to use tix
currentCurrencyAmount = priceInTix
currentCurrencyType = Enum.CurrencyType.Tix
end
elseif currentCurrencyType == Enum.CurrencyType.Tix then -- we want to sell for tix
if priceInTix ~= nil and priceInTix ~= 0 then -- we can sell for tix
currentCurrencyAmount = priceInTix
currentCurrencyType = Enum.CurrencyType.Tix
else -- have to use robux
currentCurrencyAmount = priceInRobux
currentCurrencyType = Enum.CurrencyType.Robux
end
else
return false
end
if currentCurrencyAmount == nil then
return false
end
return true
end
-- This functions checks to make sure the purchase is even possible, if not it returns false and we don't prompt user (some situations require user feedback when we won't prompt)
local function canPurchaseItem()
-- first we see if player already owns the asset/get the productinfo
local playerOwnsAsset = false
local notRightBc = false
local descText
local success = false
if purchasingConsumable then
local currentProductInfoRaw
success = ypcall(function()
currentProductInfoRaw = Game:HttpGetAsync(
`{getSecureApiBaseUrl()}marketplace/productDetails?productid={currentProductId}`
)
end)
if success then
currentProductInfo =
getRbxUtility().DecodeJSON(currentProductInfoRaw)
end
else
success = ypcall(function()
currentProductInfo =
MarketplaceService:GetProductInfo(currentAssetId)
end)
end
if currentProductInfo == nil or not success then
descText =
"In-game sales are temporarily disabled. Please try again later."
return true, nil, nil, true, descText
end
if not purchasingConsumable then
if not currentAssetId then
print "current asset id is nil, this should always have a value"
return false
end
if currentAssetId <= 0 then
print "current asset id is negative, this should always be positive"
return false
end
local success2, errorCode = ypcall(function()
playerOwnsAsset = game:HttpGetAsync(
`{getSecureApiBaseUrl()}ownership/hasAsset?userId={game.Players.LocalPlayer.userId}&assetId={currentAssetId}`
)
end)
if not success2 then
print("could not tell if player owns asset because", errorCode)
return false
end
if playerOwnsAsset == true or playerOwnsAsset == "true" then
descText = "You already own this item."
return true, nil, nil, true, descText
end
end
purchaseDialog.BodyFrame.AfterBalanceButton.Visible = true
-- next we parse through product info and see if we can purchase
if type(currentProductInfo) ~= "table" then
currentProductInfo = getRbxUtility().DecodeJSON(currentProductInfo)
end
if not currentProductInfo then
descText = "Could not get product info. Please try again later."
return true, nil, nil, true, descText
end
if
currentProductInfo.IsForSale == false
and currentProductInfo.IsPublicDomain == false
then
descText = "This item is no longer for sale."
return true, nil, nil, true, descText
end
-- now we start talking money, making sure we are going to be able to purchase this
if
not setCurrencyAmountAndType(
tonumber(currentProductInfo.PriceInRobux),
tonumber(currentProductInfo.PriceInTickets)
)
then
descText =
"We could retrieve the price of the item correctly. Please try again later."
return true, nil, nil, true, descText
end
local playerBalance = getPlayerBalance()
if not playerBalance then
descText = "Could not retrieve your balance. Please try again later."
return true, nil, nil, true, descText
end
if
tonumber(currentProductInfo.MinimumMembershipLevel)
> membershipTypeToNumber(game.Players.LocalPlayer.MembershipType)
then
notRightBc = true
end
local updatedBalance, insufficientFunds =
updateAfterBalanceText(playerBalance, notRightBc)
if notRightBc then
purchaseDialog.BodyFrame.AfterBalanceButton.Active = true
return true, insufficientFunds, notRightBc, false
end
if currentProductInfo.ContentRatingTypeId == 1 then
if game.Players.LocalPlayer:GetUnder13() then
descText =
"Your account is under 13 so purchase of this item is not allowed."
return true, nil, nil, true, descText
end
end
if
(
currentProductInfo.IsLimited == true
or currentProductInfo.IsLimitedUnique == true
)
and (
currentProductInfo.Remaining == ""
or currentProductInfo.Remaining == 0
or currentProductInfo.Remaining == nil
)
then
descText =
"All copies of this item have been sold out! Try buying from other users on the website."
return true, nil, nil, true, descText
end
if not updatedBalance then
descText =
"Could not update your balance. Please check back after some time."
return true, nil, nil, true, descText
end
-- we use insufficient funds to display a prompt to buy more robux
purchaseDialog.BodyFrame.AfterBalanceButton.Active = true
return true, insufficientFunds
end
local function doPlayerFundsCheck(checkIndefinitely)
if checkingPlayerFunds then
local canPurchase, insufficientFunds = canPurchaseItem() -- check again to see if we can buy item
if canPurchase and insufficientFunds then -- wait a bit and try a few more times
local retries = 1000
while
(retries > 0 or checkIndefinitely)
and insufficientFunds
and checkingPlayerFunds
and canPurchase
do
wait(0.1)
canPurchase, insufficientFunds = canPurchaseItem()
retries -= 1
end
end
if canPurchase and not insufficientFunds then
-- we can buy item! set our buttons up and we will exit this loop
setButtonsVisible(
purchaseDialog.BodyFrame.BuyButton,
purchaseDialog.BodyFrame.CancelButton,
purchaseDialog.BodyFrame.AfterBalanceButton
)
end
end
end
local function openBCUpSellWindow()
GuiService:OpenBrowserWindow(
`{baseUrl}Upgrades/BuildersClubMemberships.aspx`
)
end
-- user pressed the cancel button, just remove all purchasing prompts
local function showPurchasePrompt()
local canPurchase, insufficientFunds, notRightBC, override, descText =
canPurchaseItem()
if canPurchase then
updatePurchasePromptData()
if override and descText then
purchaseDialog.BodyFrame.ItemPreview.ItemDescription.Text = descText
purchaseDialog.BodyFrame.AfterBalanceButton.Visible = false
end
game.GuiService:AddCenterDialog(
purchaseDialog,
Enum.CenterDialogType.ModalDialog,
--ShowFunction
function()
-- set the state for our buttons
purchaseDialog.Visible = true
if isFreeItem() then
setButtonsVisible(
purchaseDialog.BodyFrame.FreeButton,
purchaseDialog.BodyFrame.CancelButton,
purchaseDialog.BodyFrame.AfterBalanceButton
)
elseif notRightBC then
purchaseDialog.BodyFrame.AfterBalanceButton.Text =
"You require an upgrade to your Builders Club membership to purchase this item. Click here to upgrade."
if not openBCUpSellWindowConnection then
openBCUpSellWindowConnection = purchaseDialog.BodyFrame.AfterBalanceButton.MouseButton1Click:connect(
function()
if
purchaseDialog.BodyFrame.AfterBalanceButton.Text
== "You require an upgrade to your Builders Club membership to purchase this item. Click here to upgrade."
then
openBCUpSellWindow()
end
end
)
end
setButtonsVisible(
purchaseDialog.BodyFrame.BuyDisabledButton,
purchaseDialog.BodyFrame.CancelButton,
purchaseDialog.BodyFrame.AfterBalanceButton
)
elseif insufficientFunds then
setButtonsVisible(
purchaseDialog.BodyFrame.BuyDisabledButton,
purchaseDialog.BodyFrame.CancelButton,
purchaseDialog.BodyFrame.AfterBalanceButton
)
elseif override then
setButtonsVisible(
purchaseDialog.BodyFrame.BuyDisabledButton,
purchaseDialog.BodyFrame.CancelButton
) -- , purchaseDialog.BodyFrame.AfterBalanceButton)
else
setButtonsVisible(
purchaseDialog.BodyFrame.BuyButton,
purchaseDialog.BodyFrame.CancelButton
) -- , purchaseDialog.BodyFrame.AfterBalanceButton)
end
purchaseDialog:TweenPosition(
showPosition,
Enum.EasingDirection.Out,
Enum.EasingStyle.Quad,
tweenTime,
true
)
if
canPurchase
and insufficientFunds
and not enableBrowserWindowClosedEvent
then
checkingPlayerFunds = true
doPlayerFundsCheck(true)
end
end,
--HideFunction
function()
purchaseDialog.Visible = false
end
)
else -- we failed in prompting a purchase, do a decline
userPurchaseActionsEnded(false)
end
end
-- given an asset id, this function will grab that asset from the website, and return the first "Tool" object found inside it
local function getToolAssetID(assetID)
local newTool = game:GetService("InsertService"):LoadAsset(assetID)
if not newTool then
return nil
end
if newTool:IsA "Tool" then
return newTool
end
local toolChildren = newTool:GetChildren()
for i = 1, #toolChildren do
if toolChildren[i]:IsA "Tool" then
return toolChildren[i]
end
end
return nil
end
-- the user tried to purchase by clicking the purchase button, but something went wrong.
-- let them know their account was not charged, and that they do not own the item yet.
local function purchaseFailed(inGamePurchasesDisabled)
local name = "Item"
if currentProductInfo then
name = currentProductInfo.Name
end
local newPurchasedFailedText =
string.gsub(purchaseFailedText, "itemName", tostring(name))
if inGamePurchasesDisabled then
newPurchasedFailedText = string.gsub(
newPurchasedFailedText,
"errorReason",
tostring(errorPurchasesDisabledText)
)
else
newPurchasedFailedText = string.gsub(
newPurchasedFailedText,
"errorReason",
tostring(errorPurchasesUnknownText)
)
end
purchaseDialog.BodyFrame.ItemPreview.ItemDescription.Text =
newPurchasedFailedText
purchaseDialog.BodyFrame.ItemPreview.Image = errorImageUrl
setButtonsVisible(purchaseDialog.BodyFrame.OkButton)
setHeaderText(buyFailedHeaderText)
hidePurchasing()
end
local function startSpinner()
spinning = true
Spawn(function()
local spinPos = 0
while spinning do
local pos = 0
while pos < 8 do
if pos == spinPos or pos == ((spinPos + 1) % 8) then
spinnerIcons[pos + 1].Image = assetUrl(45880668)
else
spinnerIcons[pos + 1].Image = assetUrl(45880710)
end
pos += 1
end
spinPos = (spinPos + 1) % 8
wait(1 / 15)
end
end)
end
-- user has specified they want to buy an item, now try to attempt to buy it for them
local function acceptPurchase()
-- shows a purchasing ui (shows spinner)
startSpinner()
purchaseDialog.PurchasingFrame.Visible = true
local startTime = tick()
-- http call to do the purchase
local response = "none"
local url = `{getSecureApiBaseUrl()}marketplace/`
local currencyData = `&currencyTypeId={currencyEnumToInt(
currentCurrencyType
)}&currencyTypeId={currencyEnumToInt(
currentCurrencyType
)}`
-- consumables need to use a different url
if purchasingConsumable then
url ..= `submitpurchase?productId={currentProductId}{currencyData}&expectedUnitPrice={currentCurrencyAmount}&placeId={Game.PlaceId}`
else
url ..= `purchase?productId={currentProductId}{currencyData}&purchasePrice={currentCurrencyAmount}&locationType=Game&locationId={Game.PlaceId}`
end
local success, reason = ypcall(function()
response = game:HttpPostAsync(url, "RobloxPurchaseRequest")
end)
-- debug output for us (found in the logs from local)
print(
"acceptPurchase success from ypcall is ",
success,
"reason is",
reason
)
if tick() - startTime < 1 then
wait(1) -- allow the purchasing waiting dialog to at least be readable (otherwise it might flash, looks bad)...
end
-- check to make sure purchase actually happened on the web end
if response == "none" or response == nil or response == "" then
print(
"did not get a proper response from web on purchase of",
currentAssetId,
currentProductId
)
purchaseFailed()
return
end
-- parse our response, decide how to react
response = getRbxUtility().DecodeJSON(response)
if response then
if response.success == false then
if response.status ~= "AlreadyOwned" then
print(
"web return response of fail on purchase of",
currentAssetId,
currentProductId
)
purchaseFailed((response.status == "EconomyDisabled"))
return
end
end
else
print(
"web return response of non parsable JSON on purchase of",
currentAssetId
)
purchaseFailed()
return
end
-- check to see if this item was bought, and if we want to equip it (also need to make sure the asset type was gear)
if
currentEquipOnPurchase
and success
and currentAssetId
and tonumber(currentProductInfo.AssetTypeId) == 19
then
local tool = getToolAssetID(tonumber(currentAssetId))
if tool then
tool.Parent = game.Players.LocalPlayer.Backpack
end
end
if purchasingConsumable then
if not response.receipt then
print(
"tried to buy productId, but no receipt returned. productId was",
currentProductId
)
purchaseFailed()
return
end
MarketplaceService:SignalClientPurchaseSuccess(
tostring(response.receipt),
game.Players.LocalPlayer.userId,
currentProductId
)
else
userPurchaseActionsEnded(success)
end
end
-------------------------------- End Accept/Decline Functions --------------------------------------
---------------------------------------------- Gui Functions ----------------------------------------------
-- used for the "Purchasing..." frame
local function createSpinner(size, position, parent)
local spinnerFrame = Instance.new "Frame"
spinnerFrame.Name = "Spinner"
spinnerFrame.Size = size
spinnerFrame.Position = position
spinnerFrame.BackgroundTransparency = 1
spinnerFrame.ZIndex = 10
spinnerFrame.Parent = parent
spinnerIcons = {}
local spinnerNum = 1
while spinnerNum <= 8 do
local spinnerImage = Instance.new "ImageLabel"
spinnerImage.Name = `Spinner{spinnerNum}`
spinnerImage.Size = UDim2.new(0, 16, 0, 16)
spinnerImage.Position = UDim2.new(
0.5 + 0.3 * math.cos(math.rad(45 * spinnerNum)),
-8,
0.5 + 0.3 * math.sin(math.rad(45 * spinnerNum)),
-8
)
spinnerImage.BackgroundTransparency = 1
spinnerImage.ZIndex = 10
spinnerImage.Image = assetUrl(45880710)
spinnerImage.Parent = spinnerFrame
spinnerIcons[spinnerNum] = spinnerImage
spinnerNum += 1
end
end
local function userPurchaseProductActionsEnded(userIsClosingDialog)
checkingPlayerFunds = false
if userIsClosingDialog then
closePurchasePrompt()
if currentServerResponseTable then
local isPurchased = false
if
tostring(currentServerResponseTable.isValid):lower()
== "true"
then
isPurchased = true
end
MarketplaceService:SignalPromptProductPurchaseFinished(
tonumber(currentServerResponseTable.playerId),
tonumber(currentServerResponseTable.productId),
isPurchased
)
else
print "Something went wrong, no currentServerResponseTable"
end
removeCurrentPurchaseInfo()
else
local newPurchasedSucceededText = string.gsub(
purchaseSucceededText,
"itemName",
tostring(currentProductInfo.Name)
)
purchaseDialog.BodyFrame.ItemPreview.ItemDescription.Text =
newPurchasedSucceededText
setButtonsVisible(purchaseDialog.BodyFrame.OkPurchasedButton)
hidePurchasing()
end
end
local function createImageButton(name)
local imageButton = Instance.new "ImageButton"
imageButton.Size = UDim2.new(0, 153, 0, 46)
imageButton.Name = name
return imageButton
end
local function createTextObject(name, text, type, size)
local textLabel = Instance.new(type)
textLabel.Font = Enum.Font.ArialBold
textLabel.TextColor3 = Color3.new(217 / 255, 217 / 255, 217 / 255)
textLabel.TextWrapped = true
textLabel.Name = name
textLabel.Text = text
textLabel.BackgroundTransparency = 1
textLabel.BorderSizePixel = 0
textLabel.FontSize = size
return textLabel
end
-- all the gui init. Would be nice if this didn't have to be a script
local function createPurchasePromptGui()
purchaseDialog = Instance.new "Frame"
purchaseDialog.Name = "PurchaseFrame"
purchaseDialog.Size = UDim2.new(0, 660, 0, 400)
purchaseDialog.Position = hidePosition
purchaseDialog.Visible = false
purchaseDialog.BackgroundColor3 =
Color3.new(141 / 255, 141 / 255, 141 / 255)
purchaseDialog.BorderColor3 = Color3.new(204 / 255, 204 / 255, 204 / 255)
purchaseDialog.Parent = game.CoreGui.RobloxGui
local bodyFrame = Instance.new "Frame"
bodyFrame.Name = "BodyFrame"
bodyFrame.Size = UDim2.new(1, 0, 1, -60)
bodyFrame.Position = UDim2.new(0, 0, 0, 60)
bodyFrame.BackgroundColor3 = Color3.new(67 / 255, 67 / 255, 67 / 255)
bodyFrame.BorderSizePixel = 0
bodyFrame.ZIndex = 8
bodyFrame.Parent = purchaseDialog
local titleLabel = createTextObject(
"TitleLabel",
"Buy Item",
"TextLabel",
Enum.FontSize.Size48
)
titleLabel.ZIndex = 8
titleLabel.Size = UDim2.new(1, 0, 0, 60)
local titleBackdrop = titleLabel:Clone()
titleBackdrop.Name = "TitleBackdrop"
titleBackdrop.TextColor3 = Color3.new(32 / 255, 32 / 255, 32 / 255)
titleBackdrop.BackgroundTransparency = 0.0
titleBackdrop.BackgroundColor3 = Color3.new(54 / 255, 96 / 255, 171 / 255)
titleBackdrop.Position = UDim2.new(0, 0, 0, -2)
titleBackdrop.ZIndex = 8
titleBackdrop.Parent = purchaseDialog
titleLabel.Parent = purchaseDialog
local distanceBetweenButtons = 90
local cancelButton = createImageButton "CancelButton"
cancelButton.Position =
UDim2.new(0.5, (distanceBetweenButtons / 2), 1, -120)
cancelButton.BackgroundTransparency = 1
cancelButton.BorderSizePixel = 0
cancelButton.Parent = bodyFrame
cancelButton.Modal = true
cancelButton.ZIndex = 8
cancelButton.Image = cancelButtonImageUrl
cancelButton.MouseButton1Down:connect(function()
cancelButton.Image = cancelButtonDownUrl
end)
cancelButton.MouseButton1Up:connect(function()
cancelButton.Image = cancelButtonImageUrl
end)
cancelButton.MouseLeave:connect(function()
cancelButton.Image = cancelButtonImageUrl
end)
cancelButton.MouseButton1Click:connect(function()
-- user pressed the cancel button, just remove all purchasing prompts
userPurchaseActionsEnded(false)
end)
local buyButton = createImageButton "BuyButton"
buyButton.Position =
UDim2.new(0.5, -153 - (distanceBetweenButtons / 2), 1, -120)
buyButton.BackgroundTransparency = 1
buyButton.BorderSizePixel = 0
buyButton.Image = buyImageUrl
buyButton.ZIndex = 8
buyButton.MouseButton1Down:connect(function()
buyButton.Image = buyImageDownUrl
end)
buyButton.MouseButton1Up:connect(function()
buyButton.Image = buyImageUrl
end)
buyButton.MouseLeave:connect(function()
buyButton.Image = buyImageUrl
end)
buyButton.Parent = bodyFrame
local buyDisabledButton = buyButton:Clone()
buyDisabledButton.Name = "BuyDisabledButton"
buyDisabledButton.AutoButtonColor = false
buyDisabledButton.Visible = false
buyDisabledButton.Active = false
buyDisabledButton.Image = buyImageDisabledUrl
buyDisabledButton.ZIndex = 8
buyDisabledButton.Parent = bodyFrame
local freeButton = buyButton:Clone()
freeButton.BackgroundTransparency = 1
freeButton.Name = "FreeButton"
freeButton.Visible = false
freeButton.ZIndex = 8
freeButton.Image = freeButtonImageUrl
freeButton.MouseButton1Down:connect(function()
freeButton.Image = freeButtonImageDownUrl
end)
freeButton.MouseButton1Up:connect(function()
freeButton.Image = freeButtonImageUrl
end)
freeButton.MouseLeave:connect(function()
freeButton.Image = freeButtonImageUrl
end)
freeButton.Parent = bodyFrame
local okButton = buyButton:Clone()
okButton.Name = "OkButton"
okButton.BackgroundTransparency = 1
okButton.Visible = false
okButton.Position = UDim2.new(0.5, -okButton.Size.X.Offset / 2, 1, -120)
okButton.Modal = true
okButton.Image = okButtonUrl
okButton.ZIndex = 8
okButton.MouseButton1Down:connect(function()
okButton.Image = okButtonPressedrl
end)
okButton.MouseButton1Up:connect(function()
okButton.Image = okButtonUrl
end)
okButton.MouseLeave:connect(function()
okButton.Image = okButtonUrl
end)
okButton.Parent = bodyFrame
local okPurchasedButton = okButton:Clone()
okPurchasedButton.ZIndex = 8
okPurchasedButton.Name = "OkPurchasedButton"
okPurchasedButton.MouseButton1Down:connect(function()
okPurchasedButton.Image = okButtonPressedrl
end)
okPurchasedButton.MouseButton1Up:connect(function()
okPurchasedButton.Image = okButtonUrl
end)
okPurchasedButton.MouseLeave:connect(function()
okPurchasedButton.Image = okButtonUrl
end)
okPurchasedButton.Parent = bodyFrame
okButton.MouseButton1Click:connect(function()
userPurchaseActionsEnded(false)
end)
okPurchasedButton.MouseButton1Click:connect(function()
if purchasingConsumable then
userPurchaseProductActionsEnded(true)
else
signalPromptEnded(true)
end
end)
buyButton.MouseButton1Click:connect(function()
acceptPurchase()
end)
freeButton.MouseButton1Click:connect(function()
acceptPurchase()
end)
local itemPreview = Instance.new "ImageLabel"
itemPreview.Name = "ItemPreview"
itemPreview.BackgroundColor3 = Color3.new(32 / 255, 32 / 255, 32 / 255)
itemPreview.BorderColor3 = Color3.new(141 / 255, 141 / 255, 141 / 255)
itemPreview.Position = UDim2.new(0, 30, 0, 20)
itemPreview.Size = UDim2.new(0, 180, 0, 180)
itemPreview.ZIndex = 9
itemPreview.Parent = bodyFrame
local itemDescription = createTextObject(
"ItemDescription",
"Would you like to buy the 'itemName' for currencyType currencyAmount?",
"TextLabel",
Enum.FontSize.Size24
)
itemDescription.Position = UDim2.new(1, 20, 0, 0)
itemDescription.Size = UDim2.new(0, 410, 1, 0)
itemDescription.ZIndex = 8
itemDescription.Parent = itemPreview
local afterBalanceButton = createTextObject(
"AfterBalanceButton",
"Place holder text ip sum lorem dodo ashs",
"TextButton",
Enum.FontSize.Size24
)
afterBalanceButton.AutoButtonColor = false
afterBalanceButton.TextColor3 = Color3.new(222 / 255, 59 / 255, 30 / 255)
afterBalanceButton.Position = UDim2.new(0, 5, 1, -55)
afterBalanceButton.Size = UDim2.new(1, -10, 0, 50)
afterBalanceButton.ZIndex = 8
afterBalanceButton.Parent = bodyFrame
local purchasingFrame = Instance.new "Frame"
purchasingFrame.Name = "PurchasingFrame"
purchasingFrame.Size = UDim2.new(1, 0, 1, 0)
purchasingFrame.BackgroundColor3 = Color3.new(0, 0, 0)
purchasingFrame.BackgroundTransparency = 0.2
purchasingFrame.BorderSizePixel = 0
purchasingFrame.ZIndex = 9
purchasingFrame.Visible = false
purchasingFrame.Active = true
purchasingFrame.Parent = purchaseDialog
local purchasingLabel = createTextObject(
"PurchasingLabel",
"Purchasing...",
"TextLabel",
Enum.FontSize.Size48
)
purchasingLabel.Size = UDim2.new(1, 0, 1, 0)
purchasingLabel.ZIndex = 10
purchasingLabel.Parent = purchasingFrame
createSpinner(
UDim2.new(0, 50, 0, 50),
UDim2.new(0.5, -25, 0.5, 30),
purchasingLabel
)
end
-- next two functions control the "Purchasing..." overlay
-- next 2 functions are convenienvce creation functions for guis
local function cutSizeInHalfRecursive(_)
-- todo: change the gui size based on how much space we have
--[[changeSize(instance,0.5)
local children = instance:GetChildren()
for i = 1, #children do
cutSizeInHalfRecursive(children[i])
end]]
end
local function doubleSizeRecursive(_)
-- todo: change the gui size based on how much space we have
--[[changeSize(instance,2)
local children = instance:GetChildren()
for i = 1, #children do
doubleSizeRecursive(children[i])
end]]
end
local function modifyForSmallScreen()
cutSizeInHalfRecursive(purchaseDialog)
end
local function modifyForLargeScreen()
doubleSizeRecursive(purchaseDialog)
end
-- depending on screen size, we need to change the gui
local function changeGuiToScreenSize(smallScreen)
if smallScreen then
modifyForSmallScreen()
else
modifyForLargeScreen()
end
end
local function doPurchasePrompt(
player,
assetId,
equipIfPurchased,
currencyType,
productId
)
if not purchaseDialog then
createPurchasePromptGui()
end
if player == game.Players.LocalPlayer then
if currentlyPrompting then
return
end
currentlyPrompting = true
currentAssetId = assetId
currentProductId = productId
currentCurrencyType = currencyType
currentEquipOnPurchase = equipIfPurchased
purchasingConsumable = (currentProductId ~= nil)
showPurchasePrompt()
end
end
local function doProcessServerPurchaseResponse(serverResponseTable)
if not serverResponseTable then
print "Server response table was nil, cancelling purchase"
purchaseFailed()
return
end
if
serverResponseTable.playerId
and tonumber(serverResponseTable.playerId)
== game.Players.LocalPlayer.userId
then
currentServerResponseTable = serverResponseTable
userPurchaseProductActionsEnded(false)
end
end
---------------------------------------------- End Gui Functions ----------------------------------------------
---------------------------------------------- Script Event start/initialization ----------------------------------------------
preloadAssets()
MarketplaceService.PromptProductPurchaseRequested:connect(
function(player, productId, equipIfPurchased, currencyType)
doPurchasePrompt(player, nil, equipIfPurchased, currencyType, productId)
end
)
MarketplaceService.PromptPurchaseRequested:connect(
function(player, assetId, equipIfPurchased, currencyType)
doPurchasePrompt(player, assetId, equipIfPurchased, currencyType, nil)
end
)
MarketplaceService.ServerPurchaseVerification:connect(
function(serverResponseTable)
doProcessServerPurchaseResponse(serverResponseTable)
end
)
if enableBrowserWindowClosedEvent then
GuiService.BrowserWindowClosed:connect(function()
doPlayerFundsCheck(false)
end)
end
Game.CoreGui.RobloxGui.Changed:connect(function()
local nowIsSmallScreen = (
game.CoreGui.RobloxGui.AbsoluteSize.Y <= smallScreenThreshold
)
if nowIsSmallScreen and not isSmallScreen then
changeGuiToScreenSize(true)
elseif not nowIsSmallScreen and isSmallScreen then
changeGuiToScreenSize(false)
end
isSmallScreen = nowIsSmallScreen
end)
isSmallScreen = (game.CoreGui.RobloxGui.AbsoluteSize.Y <= smallScreenThreshold)
if isSmallScreen then
changeGuiToScreenSize(true)
end