mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 05:07:48 +00:00
1114 lines
36 KiB
Lua
1114 lines
36 KiB
Lua
--[[
|
|
// Filename: PurchasePromptScript2.lua
|
|
// Version 1.0
|
|
// Release 186
|
|
// Written by: jeditkacheff/jmargh
|
|
// Description: Handles in game purchases
|
|
]]--
|
|
--[[ Services ]]--
|
|
local GuiService = game:GetService('GuiService')
|
|
local HttpService = game:GetService('HttpService')
|
|
local HttpRbxApiService = game:GetService('HttpRbxApiService')
|
|
local InsertService = game:GetService('InsertService')
|
|
local MarketplaceService = game:GetService('MarketplaceService')
|
|
local Players = game:GetService('Players')
|
|
local UserInputService = game:GetService('UserInputService')
|
|
|
|
--[[ Script Variables ]]--
|
|
local RobloxGui = script.Parent
|
|
local ThirdPartyProductName = nil
|
|
|
|
--[[ Flags ]]--
|
|
local IsNativePurchasing = UserInputService.TouchEnabled
|
|
local IsCurrentlyPrompting = false
|
|
local IsCurrentlyPurchasing = false
|
|
local IsPurchasingConsumable = false
|
|
local IsCheckingPlayerFunds = false
|
|
|
|
--[[ Purchase Data ]]--
|
|
local PurchaseData = {
|
|
AssetId = nil,
|
|
ProductId = nil,
|
|
CurrencyType = nil,
|
|
EquipOnPurchase = nil,
|
|
ProductInfo = nil,
|
|
ItemDescription = nil,
|
|
}
|
|
|
|
--[[ Constants ]]--
|
|
local BASE_URL = game:GetService('ContentProvider').BaseUrl:lower()
|
|
BASE_URL = string.gsub(BASE_URL, "/m.", "/www.")
|
|
local THUMBNAIL_URL = BASE_URL.."thumbs/asset.ashx?assetid="
|
|
-- Images
|
|
local BG_IMAGE = 'rbxasset://textures/ui/Modal.png'
|
|
local PURCHASE_BG = 'rbxasset://textures/ui/LoadingBKG.png'
|
|
local BUTTON_LEFT = 'rbxasset://textures/ui/ButtonLeft.png'
|
|
local BUTTON_LEFT_DOWN = 'rbxasset://textures/ui/ButtonLeftDown.png'
|
|
local BUTTON_RIGHT = 'rbxasset://textures/ui/ButtonRight.png'
|
|
local BUTTON_RIGHT_DOWN = 'rbxasset://textures/ui/ButtonRightDown.png'
|
|
local BUTTON = 'rbxasset://textures/ui/SingleButton.png'
|
|
local BUTTON_DOWN = 'rbxasset://textures/ui/SingleButtonDown.png'
|
|
local ROBUX_ICON = 'rbxasset://textures/ui/RobuxIcon.png'
|
|
local TIX_ICON = 'rbxasset://textures/ui/TixIcon.png'
|
|
local ERROR_ICON = 'rbxasset://textures/ui/ErrorIcon.png'
|
|
|
|
local ERROR_MSG = {
|
|
PURCHASE_DISABLED = "In-game purchases are temporarily disabled",
|
|
INVALID_FUNDS = "your account does not have enought Robux",
|
|
UNKNOWN = "Roblox is performing maintenance",
|
|
UNKNWON_FAILURE = "something went wrong"
|
|
}
|
|
local PURCHASE_MSG = {
|
|
SUCCEEDED = "Your purchase of itemName succeeded!",
|
|
FAILED = "Your purchase of itemName failed because errorReason. Your account has not been charged. Please try again later.",
|
|
PURCHASE = "Want to buy the assetType\nitemName for",
|
|
PURCHASE_TIX = "Want to buy the assetType\nitemName for",
|
|
FREE = "Would you like to take the assetType itemName for FREE?",
|
|
FREE_BALANCE = "Your account balance will not be affected by this transaction.",
|
|
BALANCE_FUTURE = "Your balance after this transaction will be ",
|
|
BALANCE_NOW = "Your balance is now ",
|
|
ALREADY_OWN = "You already own this item. Your account has not been charged."
|
|
}
|
|
local PURCHASE_FAILED = {
|
|
DEFAULT_ERROR = 0,
|
|
IN_GAME_PURCHASE_DISABLED = 1,
|
|
CANNOT_GET_BALANCE = 2,
|
|
CANNOT_GET_ITEM_PRICE = 3,
|
|
NOT_FOR_SALE = 4,
|
|
NOT_ENOUGH_TIX = 5,
|
|
UNDER_13 = 6,
|
|
LIMITED = 7,
|
|
DID_NOT_BUY_ROBUX = 8,
|
|
}
|
|
local BC_LVL_TO_STRING = {
|
|
"Builders Club",
|
|
"Turbo Builders Club",
|
|
"Outrageous Builders Club",
|
|
}
|
|
local ASSET_TO_STRING = {
|
|
[1] = "Image";
|
|
[2] = "T-Shirt";
|
|
[3] = "Audio";
|
|
[4] = "Mesh";
|
|
[5] = "Lua";
|
|
[6] = "HTML";
|
|
[7] = "Text";
|
|
[8] = "Hat";
|
|
[9] = "Place";
|
|
[10] = "Model";
|
|
[11] = "Shirt";
|
|
[12] = "Pants";
|
|
[13] = "Decal";
|
|
[16] = "Avatar";
|
|
[17] = "Head";
|
|
[18] = "Face";
|
|
[19] = "Gear";
|
|
[21] = "Badge";
|
|
[22] = "Group Emblem";
|
|
[24] = "Animation";
|
|
[25] = "Arms";
|
|
[26] = "Legs";
|
|
[27] = "Torso";
|
|
[28] = "Right Arm";
|
|
[29] = "Left Arm";
|
|
[30] = "Left Leg";
|
|
[31] = "Right Leg";
|
|
[32] = "Package";
|
|
[33] = "YouTube Video";
|
|
[34] = "Game Pass";
|
|
[38] = "Plugin";
|
|
[0] = "Product";
|
|
}
|
|
local BC_ROBUX_PRODUCTS = { 90, 180, 270, 360, 450, 1000, 2750 }
|
|
local NON_BC_ROBUX_PRODUCTS = { 80, 160, 240, 320, 400, 800, 2000 }
|
|
|
|
local DIALOG_SIZE = UDim2.new(0, 324, 0, 180)
|
|
local SHOW_POSITION = UDim2.new(0.5, -162, 0.5, -90)
|
|
local HIDE_POSITION = UDim2.new(0.5, -162, 0, -181)
|
|
local BTN_SIZE = UDim2.new(0, 162, 0, 44)
|
|
local BODY_SIZE = UDim2.new(0, 324, 0, 136)
|
|
local TWEEN_TIME = 0.3
|
|
|
|
local BTN_L_POS = UDim2.new(0, 0, 0, 136)
|
|
local BTN_R_POS = UDim2.new(0.5, 0, 0, 136)
|
|
|
|
--[[ Utility Functions ]]--
|
|
local function lerp( start, finish, t)
|
|
return (1 - t) * start + t * finish
|
|
end
|
|
|
|
local function formatNumber(value)
|
|
return tostring(value):reverse():gsub("%d%d%d", "%1,"):reverse():gsub("^,", "")
|
|
end
|
|
|
|
--[[ Gui Creation Functions ]]--
|
|
local function createFrame(name, size, position, bgTransparency, bgColor)
|
|
local frame = Instance.new('Frame')
|
|
frame.Name = name
|
|
frame.Size = size
|
|
frame.Position = position or UDim2.new(0, 0, 0, 0)
|
|
frame.BackgroundTransparency = bgTransparency
|
|
frame.BackgroundColor3 = bgColor or Color3.new()
|
|
frame.BorderSizePixel = 0
|
|
frame.ZIndex = 8
|
|
|
|
return frame
|
|
end
|
|
|
|
local function createTextLabel(name, size, position, font, fontSize, text)
|
|
local textLabel = Instance.new('TextLabel')
|
|
textLabel.Name = name
|
|
textLabel.Size = size or UDim2.new(0, 0, 0, 0)
|
|
textLabel.Position = position
|
|
textLabel.BackgroundTransparency = 1
|
|
textLabel.Font = font
|
|
textLabel.FontSize = fontSize
|
|
textLabel.TextColor3 = Color3.new(1, 1, 1)
|
|
textLabel.Text = text
|
|
textLabel.ZIndex = 8
|
|
|
|
return textLabel
|
|
end
|
|
|
|
local function createImageLabel(name, size, position, image)
|
|
local imageLabel = Instance.new('ImageLabel')
|
|
imageLabel.Name = name
|
|
imageLabel.Size = size
|
|
imageLabel.BackgroundTransparency = 1
|
|
imageLabel.Position = position
|
|
imageLabel.Image = image
|
|
|
|
return imageLabel
|
|
end
|
|
|
|
local function createImageButtonWithText(name, position, image, imageDown, text, font)
|
|
local imageButton = Instance.new('ImageButton')
|
|
imageButton.Name = name
|
|
imageButton.Size = BTN_SIZE
|
|
imageButton.Position = position
|
|
imageButton.Image = image
|
|
imageButton.BackgroundTransparency = 1
|
|
imageButton.AutoButtonColor = false
|
|
imageButton.ZIndex = 8
|
|
|
|
local textLabel = createTextLabel(name.."Text", UDim2.new(1, 0, 1, 0), UDim2.new(0, 0, 0, 0), font, Enum.FontSize.Size24, text)
|
|
textLabel.ZIndex = 9
|
|
textLabel.Parent = imageButton
|
|
|
|
imageButton.MouseEnter:connect(function()
|
|
imageButton.Image = imageDown
|
|
end)
|
|
imageButton.MouseLeave:connect(function()
|
|
imageButton.Image = image
|
|
end)
|
|
imageButton.MouseButton1Click:connect(function()
|
|
imageButton.Image = image
|
|
end)
|
|
|
|
return imageButton
|
|
end
|
|
|
|
--[[ Begin Gui Creation ]]--
|
|
local PurchaseDialog = createFrame("PurchaseDialog", DIALOG_SIZE, HIDE_POSITION, 1, nil)
|
|
PurchaseDialog.Visible = false
|
|
PurchaseDialog.Parent = RobloxGui
|
|
|
|
local ContainerFrame = createFrame("ContainerFrame", UDim2.new(1, 0, 1, 0), nil, 1, nil)
|
|
ContainerFrame.Parent = PurchaseDialog
|
|
|
|
local ContainerImage = createImageLabel("ContainerImage", BODY_SIZE, UDim2.new(0, 0, 0, 0), BG_IMAGE)
|
|
ContainerImage.ZIndex = 8
|
|
ContainerImage.Parent = ContainerFrame
|
|
|
|
local ItemPreviewImage = createImageLabel("ItemPreviewImage", UDim2.new(0, 64, 0, 64), UDim2.new(0, 27, 0, 20), "")
|
|
ItemPreviewImage.ZIndex = 9
|
|
ItemPreviewImage.Parent = ContainerFrame
|
|
|
|
local ItemDescriptionText = createTextLabel("ItemDescriptionText", UDim2.new(0, 210, 0, 96), UDim2.new(0, 110, 0, 18),
|
|
Enum.Font.SourceSans, Enum.FontSize.Size18, PURCHASE_MSG.PURCHASE)
|
|
ItemDescriptionText.TextXAlignment = Enum.TextXAlignment.Left
|
|
ItemDescriptionText.TextYAlignment = Enum.TextYAlignment.Top
|
|
ItemDescriptionText.TextWrapped = true
|
|
ItemDescriptionText.Parent = ContainerFrame
|
|
|
|
local RobuxIcon = createImageLabel("RobuxIcon", UDim2.new(0, 20, 0, 20), UDim2.new(0, 0, 0, 0), ROBUX_ICON)
|
|
RobuxIcon.ZIndex = 9
|
|
RobuxIcon.Visible = false
|
|
RobuxIcon.Parent = ContainerFrame
|
|
|
|
local TixIcon = createImageLabel("TixIcon", UDim2.new(0, 20, 0, 20), UDim2.new(0, 0, 0, 0), TIX_ICON)
|
|
TixIcon.ZIndex = 9
|
|
TixIcon.Visible = false
|
|
TixIcon.Parent = ContainerFrame
|
|
|
|
local CostText = createTextLabel("CostText", UDim2.new(0, 0, 0, 0), UDim2.new(0, 0, 0, 0),
|
|
Enum.Font.SourceSansBold, Enum.FontSize.Size18, "")
|
|
CostText.TextXAlignment = Enum.TextXAlignment.Left
|
|
CostText.Visible = false
|
|
CostText.Parent = ContainerFrame
|
|
|
|
local PostBalanceText = createTextLabel("PostBalanceText", UDim2.new(1, -20, 0, 30), UDim2.new(0, 10, 0, 100), Enum.Font.SourceSans,
|
|
Enum.FontSize.Size14, "")
|
|
PostBalanceText.TextWrapped = true
|
|
PostBalanceText.Parent = ContainerFrame
|
|
|
|
local BuyButton = createImageButtonWithText("BuyButton", BTN_L_POS, BUTTON_LEFT, BUTTON_LEFT_DOWN, "Buy Now", Enum.Font.SourceSansBold)
|
|
BuyButton.Parent = ContainerFrame
|
|
|
|
local CancelButton = createImageButtonWithText("CancelButton", BTN_R_POS, BUTTON_RIGHT, BUTTON_RIGHT_DOWN, "Cancel", Enum.Font.SourceSans)
|
|
CancelButton.Parent = ContainerFrame
|
|
|
|
local BuyRobuxButton = createImageButtonWithText("BuyRobuxButton", BTN_L_POS, BUTTON_LEFT, BUTTON_LEFT_DOWN, IsNativePurchasing and "Buy" or "Buy R$",
|
|
Enum.Font.SourceSansBold)
|
|
BuyRobuxButton.Visible = false
|
|
BuyRobuxButton.Parent = ContainerFrame
|
|
|
|
local BuyBCButton = createImageButtonWithText("BuyBCButton", BTN_L_POS, BUTTON_LEFT, BUTTON_LEFT_DOWN, "Upgrade", Enum.Font.SourceSansBold)
|
|
BuyBCButton.Visible = false
|
|
BuyBCButton.Parent = ContainerFrame
|
|
|
|
local FreeButton = createImageButtonWithText("FreeButton", BTN_L_POS, BUTTON_LEFT, BUTTON_LEFT_DOWN, "Take Free", Enum.Font.SourceSansBold)
|
|
FreeButton.Visible = false
|
|
FreeButton.Parent = ContainerFrame
|
|
|
|
local OkButton = createImageButtonWithText("OkButton", UDim2.new(0, 2, 0, 136), BUTTON, BUTTON_DOWN, "OK", Enum.Font.SourceSans)
|
|
OkButton.Size = UDim2.new(0, 320, 0, 44)
|
|
OkButton.Visible = false
|
|
OkButton.Parent = ContainerFrame
|
|
|
|
local OkPurchasedButton = createImageButtonWithText("OkPurchasedButton", UDim2.new(0, 2, 0, 136), BUTTON, BUTTON_DOWN, "OK", Enum.Font.SourceSans)
|
|
OkPurchasedButton.Size = UDim2.new(0, 320, 0, 44)
|
|
OkPurchasedButton.Visible = false
|
|
OkPurchasedButton.Parent = ContainerFrame
|
|
|
|
local PurchaseFrame = createImageLabel("PurchaseFrame", UDim2.new(1, 0, 1, 0), UDim2.new(0, 0, 0, 0), PURCHASE_BG)
|
|
PurchaseFrame.ZIndex = 8
|
|
PurchaseFrame.Visible = false
|
|
PurchaseFrame.Parent = PurchaseDialog
|
|
|
|
local PurchaseText = createTextLabel("PurchaseText", nil, UDim2.new(0.5, 0, 0.5, -36), Enum.Font.SourceSans,
|
|
Enum.FontSize.Size36, "Purchasing")
|
|
PurchaseText.Parent = PurchaseFrame
|
|
|
|
local LoadingFrames = {}
|
|
local xOffset = -40
|
|
for i = 1, 3 do
|
|
local frame = createFrame("Loading", UDim2.new(0, 16, 0, 16), UDim2.new(0.5, xOffset, 0.5, 0), 0, Color3.new(132/255, 132/255, 132/255))
|
|
table.insert(LoadingFrames, frame)
|
|
frame.Parent = PurchaseFrame
|
|
xOffset = xOffset + 32
|
|
end
|
|
|
|
--[[ Purchase Data Functions ]]--
|
|
local function getCurrencyString(currencyType)
|
|
return currencyType == Enum.CurrencyType.Tix and "Tix" or "R$"
|
|
end
|
|
|
|
local function setInitialPurchaseData(assetId, productId, currencyType, equipOnPurchase)
|
|
PurchaseData.AssetId = assetId
|
|
PurchaseData.ProductId = productId
|
|
PurchaseData.CurrencyType = currencyType
|
|
PurchaseData.EquipOnPurchase = equipOnPurchase
|
|
|
|
IsPurchasingConsumable = productId ~= nil
|
|
end
|
|
|
|
local function setCurrencyData(playerBalance)
|
|
local priceInRobux = tonumber(PurchaseData.ProductInfo['PriceInRobux'])
|
|
local priceInTickets = tonumber(PurchaseData.ProductInfo['PriceInTickets'])
|
|
--
|
|
if PurchaseData.CurrencyType == Enum.CurrencyType.Default or PurchaseData.CurrencyType == Enum.CurrencyType.Robux then
|
|
if priceInRobux and priceInRobux ~= 0 then
|
|
PurchaseData.CurrencyAmount = priceInRobux
|
|
PurchaseData.CurrencyType = Enum.CurrencyType.Robux
|
|
else
|
|
PurchaseData.CurrencyAmount = priceInTickets
|
|
PurchaseData.CurrencyType = Enum.CurrencyType.Tix
|
|
end
|
|
elseif PurchaseData.CurrencyType == Enum.CurrencyType.Tix then
|
|
if priceInTickets and priceInTickets ~= 0 then
|
|
PurchaseData.CurrencyAmount = priceInTickets
|
|
else
|
|
PurchaseData.CurrencyAmount = priceInRobux
|
|
PurchaseData.CurrencyType = Enum.CurrencyType.Robux
|
|
end
|
|
end
|
|
end
|
|
|
|
local function setPreviewImage(productInfo, assetId)
|
|
if IsPurchasingConsumable then
|
|
if productInfo then
|
|
ItemPreviewImage.Image = THUMBNAIL_URL..tostring(productInfo["IconImageAssetId"].."&x=100&y=100&format=png")
|
|
end
|
|
else
|
|
if assetId then
|
|
ItemPreviewImage.Image = THUMBNAIL_URL..tostring(assetId).."&x=100&y=100&format=png"
|
|
end
|
|
end
|
|
end
|
|
|
|
local function clearPurchaseData()
|
|
for k,v in pairs(PurchaseData) do
|
|
PurchaseData[k] = nil
|
|
end
|
|
RobuxIcon.Visible = false
|
|
TixIcon.Visible = false
|
|
CostText.Visible = false
|
|
end
|
|
|
|
--[[ Show Functions ]]--
|
|
local function setButtonsVisible(...)
|
|
local args = {...}
|
|
local argCount = select('#', ...)
|
|
|
|
for _,child in pairs(ContainerFrame:GetChildren()) do
|
|
if child:IsA('ImageButton') then
|
|
child.Visible = false
|
|
for i = 1, argCount do
|
|
if child == args[i] then
|
|
child.Visible = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function tweenBackgroundColor(frame, endColor, duration)
|
|
local t = 0
|
|
local prevTime = tick()
|
|
local startColor = frame.BackgroundColor3
|
|
while t < duration do
|
|
local s = t / duration
|
|
local r = lerp(startColor.r, endColor.r, s)
|
|
local g = lerp(startColor.g, endColor.g, s)
|
|
local b = lerp(startColor.b, endColor.b, s)
|
|
frame.BackgroundColor3 = Color3.new(r, g, b)
|
|
--
|
|
t = t + (tick() - prevTime)
|
|
prevTime = tick()
|
|
wait()
|
|
end
|
|
frame.BackgroundColor3 = endColor
|
|
end
|
|
|
|
local isPurchaseAnimating = false
|
|
local function startPurchaseAnimation()
|
|
if PurchaseFrame.Visible then return end
|
|
--
|
|
ContainerFrame.Visible = false
|
|
PurchaseFrame.Visible = true
|
|
--
|
|
spawn(function()
|
|
isPurchaseAnimating = true
|
|
local i = 1
|
|
while isPurchaseAnimating do
|
|
local frame = LoadingFrames[i]
|
|
local prevPosition = frame.Position
|
|
local newPosition = UDim2.new(prevPosition.X.Scale, prevPosition.X.Offset, prevPosition.Y.Scale, prevPosition.Y.Offset - 2)
|
|
spawn(function()
|
|
tweenBackgroundColor(frame, Color3.new(0, 162/255, 1), 0.25)
|
|
end)
|
|
frame:TweenSizeAndPosition(UDim2.new(0, 16, 0, 20), newPosition, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.25, true, function()
|
|
spawn(function()
|
|
tweenBackgroundColor(frame, Color3.new(132/255, 132/255, 132/255), 0.25)
|
|
end)
|
|
frame:TweenSizeAndPosition(UDim2.new(0, 16, 0, 16), prevPosition, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.25, true)
|
|
end)
|
|
i = i + 1
|
|
if i > 3 then
|
|
i = 1
|
|
wait(0.25) -- small pause when starting from 1
|
|
end
|
|
wait(0.5)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function stopPurchaseAnimation()
|
|
isPurchaseAnimating = false
|
|
PurchaseFrame.Visible = false
|
|
ContainerFrame.Visible = true
|
|
end
|
|
|
|
local function setPurchaseDataInGui(isFree, invalidBC)
|
|
local descriptionText = PurchaseData.CurrencyType == Enum.CurrencyType.Tix and PURCHASE_MSG.PURCHASE_TIX or PURCHASE_MSG.PURCHASE
|
|
if isFree then
|
|
descriptionText = PURCHASE_MSG.FREE
|
|
PostBalanceText.Text = PURCHASE_MSG.FREE_BALANCE
|
|
end
|
|
|
|
local productInfo = PurchaseData.ProductInfo
|
|
if not productInfo then
|
|
return false
|
|
end
|
|
local itemDescription = string.gsub(descriptionText, "itemName", string.sub(productInfo["Name"], 1, 20))
|
|
itemDescription = string.gsub(itemDescription, "assetType", ASSET_TO_STRING[productInfo["AssetTypeId"]] or "Unknown")
|
|
ItemDescriptionText.Text = itemDescription
|
|
|
|
if not isFree then
|
|
if PurchaseData.CurrencyType == Enum.CurrencyType.Tix then
|
|
TixIcon.Visible = true
|
|
TixIcon.Position = UDim2.new(0, 110, 0, ItemDescriptionText.Position.Y.Offset + ItemDescriptionText.TextBounds.y + 6)
|
|
CostText.TextColor3 = Color3.new(204/255, 158/255, 113/255)
|
|
else
|
|
RobuxIcon.Visible = true
|
|
RobuxIcon.Position = UDim2.new(0, 110, 0, ItemDescriptionText.Position.Y.Offset + ItemDescriptionText.TextBounds.y + 6)
|
|
CostText.TextColor3 = Color3.new(2/255, 183/255, 87/255)
|
|
end
|
|
CostText.Text = formatNumber(PurchaseData.CurrencyAmount)
|
|
CostText.Position = UDim2.new(0, 134, 0, ItemDescriptionText.Position.Y.Offset + ItemDescriptionText.TextBounds.y + 15)
|
|
CostText.Visible = true
|
|
end
|
|
|
|
setPreviewImage(productInfo, PurchaseData.AssetId)
|
|
setButtonsVisible(isFree and FreeButton or BuyButton, CancelButton)
|
|
PostBalanceText.Visible = true
|
|
|
|
if invalidBC then
|
|
local neededBcLevel = PurchaseData.ProductInfo["MinimumMembershipLevel"]
|
|
PostBalanceText.Text = "This item requires "..BC_LVL_TO_STRING[neededBcLevel]..".\nClick 'Upgrade' to upgrade your Builders Club!"
|
|
setButtonsVisible(BuyBCButton, CancelButton)
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function getRobuxProduct(amountNeeded, isBCMember)
|
|
local productArray = isBCMember and BC_ROBUX_PRODUCTS or NON_BC_ROBUX_PRODUCTS
|
|
local closestProduct = productArray[1]
|
|
local closestIndex = 1
|
|
|
|
for i = 1, #productArray do
|
|
if (math.abs(productArray[i] - amountNeeded) < math.abs(closestProduct - amountNeeded)) then
|
|
closestProduct = productArray[i]
|
|
closestIndex = i
|
|
end
|
|
end
|
|
if closestProduct < amountNeeded then
|
|
closestProduct = productArray[1 + closestIndex]
|
|
end
|
|
return closestProduct
|
|
end
|
|
|
|
local function getRobuxProductToBuyItem(amountNeeded)
|
|
local isBCMember = Players.LocalPlayer.MembershipType ~= Enum.MembershipType.None
|
|
|
|
local productCost = getRobuxProduct(amountNeeded, isBCMember)
|
|
if not productCost then
|
|
return nil
|
|
end
|
|
local success, isAndroid = pcall(function()
|
|
return UserInputService:GetPlatform() == Enum.Platform.Android
|
|
end)
|
|
if not success then
|
|
print("PurchasePromptScript: getRobuxProductToBuyItem() failed because", isAndroid)
|
|
end
|
|
|
|
local prependStr, appendStr, appPrefix = "", "", ""
|
|
if isAndroid then
|
|
prependStr = "robux"
|
|
if isBCMember then
|
|
appendStr = "bc"
|
|
end
|
|
appPrefix = "wtf.watrbx.client."
|
|
else
|
|
appendStr = isBCMember and "RobuxBC" or "RobuxNonBC"
|
|
appPrefix = "wtf.watrbx.robloxmobile."
|
|
end
|
|
|
|
local productStr = appPrefix..prependStr..tostring(productCost)..appendStr
|
|
return productStr, productCost
|
|
end
|
|
|
|
local function setBuyMoreRobuxDialog(playerBalance)
|
|
local playerBalanceInt = tonumber(playerBalance["robux"])
|
|
local neededRobux = PurchaseData.CurrencyAmount - playerBalanceInt
|
|
local productInfo = PurchaseData.ProductInfo
|
|
|
|
local descriptionText = "You need "..formatNumber(neededRobux).." more ROBUX to buy the "..productInfo["Name"].." "..
|
|
ASSET_TO_STRING[productInfo["AssetTypeId"]]
|
|
setButtonsVisible(BuyRobuxButton, CancelButton)
|
|
|
|
if IsNativePurchasing then
|
|
local productCost = nil
|
|
ThirdPartyProductName, productCost = getRobuxProductToBuyItem(neededRobux)
|
|
--
|
|
if not ThirdPartyProductName then
|
|
descriptionText = "This item cost more ROBUX than you can purchase. Please visit www.watrbx.wtf to purchase more ROBUX."
|
|
setButtonsVisible(OkButton)
|
|
else
|
|
local remainder = playerBalanceInt + productCost - PurchaseData.CurrencyAmount
|
|
descriptionText = descriptionText..". Would you like to buy "..formatNumber(productCost).." ROBUX?"
|
|
PostBalanceText.Text = "The remaining "..formatNumber(remainder).." ROBUX will be credited to your balance."
|
|
PostBalanceText.Visible = true
|
|
end
|
|
else
|
|
descriptionText = descriptionText..". Would you like to buy more ROBUX?"
|
|
end
|
|
ItemDescriptionText.Text = descriptionText
|
|
setPreviewImage(productInfo, PurchaseData.AssetId)
|
|
end
|
|
|
|
local function showPurchasePrompt()
|
|
stopPurchaseAnimation()
|
|
PurchaseDialog.Visible = true
|
|
PurchaseDialog:TweenPosition(SHOW_POSITION, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, TWEEN_TIME, true)
|
|
UserInputService.ModalEnabled = true
|
|
end
|
|
|
|
--[[ Close and Cancel Functions ]]--
|
|
local function onPurchaseFailed(failType)
|
|
setButtonsVisible(OkButton)
|
|
ItemPreviewImage.Image = ERROR_ICON
|
|
PostBalanceText.Text = ""
|
|
|
|
local itemName = PurchaseData.ProductInfo and PurchaseData.ProductInfo["Name"] or ""
|
|
local failedText = string.gsub(PURCHASE_MSG.FAILED, "itemName", string.sub(itemName, 1, 20))
|
|
if failType == PURCHASE_FAILED.DEFAULT_ERROR then
|
|
failedText = string.gsub(failedText, "errorReason", ERROR_MSG.UNKNWON_FAILURE)
|
|
elseif failType == PURCHASE_FAILED.IN_GAME_PURCHASE_DISABLED then
|
|
if itemName == "" then
|
|
failedText = string.gsub(failedText, " of ", "")
|
|
end
|
|
failedText = string.gsub(failedText, "errorReason", ERROR_MSG.PURCHASE_DISABLED)
|
|
elseif failType == PURCHASE_FAILED.CANNOT_GET_BALANCE then
|
|
failedText = "Cannot retrieve your balance at this time. Your account has not been charged. Please try again later."
|
|
elseif failType == PURCHASE_FAILED.CANNOT_GET_ITEM_PRICE then
|
|
failedText = "We couldn't retrieve the price of the item at this time. Your account has not been charged. Please try again later."
|
|
elseif failType == PURCHASE_FAILED.NOT_FOR_SALE then
|
|
failedText = "This item is not currently for sale. Your account has not been charged."
|
|
setPreviewImage(PurchaseData.ProductInfo, PurchaseData.AssetId)
|
|
elseif failType == PURCHASE_FAILED.NOT_ENOUGH_TIX then
|
|
failedText = "This item cost more tickets than you currently have. Try trading currency on www.watrbx.wtf to get more tickets."
|
|
setPreviewImage(PurchaseData.ProductInfo, PurchaseData.AssetId)
|
|
elseif failType == PURCHASE_FAILED.UNDER_13 then
|
|
failedText = "Your account is under 13. Purchase of this item is not allowed. Your account has not been charged."
|
|
elseif failType == PURCHASE_FAILED.LIMITED then
|
|
failedText = "This limited item has no more copies. Try buying from another user on www.watrbx.wtf. Your account has not been charged."
|
|
setPreviewImage(PurchaseData.ProductInfo, PurchaseData.AssetId)
|
|
elseif failType == PURCHASE_FAILED.DID_NOT_BUY_ROBUX then
|
|
failedText = string.gsub(failedText, "errorReason", ERROR_MSG.INVALID_FUNDS)
|
|
end
|
|
|
|
ItemDescriptionText.Text = failedText
|
|
showPurchasePrompt()
|
|
end
|
|
|
|
local function closePurchaseDialog()
|
|
PurchaseDialog:TweenPosition(HIDE_POSITION, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, TWEEN_TIME, true, function()
|
|
PurchaseDialog.Visible = false
|
|
IsCurrentlyPrompting = false
|
|
IsCurrentlyPurchasing = false
|
|
IsCheckingPlayerFunds = false
|
|
UserInputService.ModalEnabled = false
|
|
end)
|
|
end
|
|
|
|
-- Main exit point
|
|
local function onPromptEnded(isSuccess)
|
|
closePurchaseDialog()
|
|
if IsPurchasingConsumable then
|
|
MarketplaceService:SignalPromptProductPurchaseFinished(Players.LocalPlayer.userId, PurchaseData.ProductId, isSuccess)
|
|
else
|
|
MarketplaceService:SignalPromptPurchaseFinished(Players.LocalPlayer, PurchaseData.AssetId, isSuccess)
|
|
end
|
|
clearPurchaseData()
|
|
end
|
|
|
|
--[[ Purchase Validation ]]--
|
|
local function isMarketplaceDown() -- FFlag
|
|
local success, result = pcall(function() return settings():GetFFlag('Order66') end)
|
|
if not success then
|
|
print("PurchasePromptScript: isMarketplaceDown failed because", result)
|
|
return false
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
local function checkMarketplaceAvailable() -- FFlag
|
|
local success, result = pcall(function() return settings():GetFFlag("CheckMarketplaceAvailable") end)
|
|
if not success then
|
|
print("PurchasePromptScript: checkMarketplaceAvailable failed because", result)
|
|
return false
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
-- return success and isAvailable
|
|
local function isMarketplaceAvailable()
|
|
local success, result = pcall(function()
|
|
return HttpRbxApiService:GetAsync("my/economy-status", false,
|
|
Enum.ThrottlingPriority.Extreme)
|
|
end)
|
|
if not success then
|
|
print("PurchasePromptScript: isMarketplaceUnavailable() failed because", result)
|
|
return false
|
|
end
|
|
result = HttpService:JSONDecode(result)
|
|
if result["isMarketplaceEnabled"] ~= nil then
|
|
if result["isMarketplaceEnabled"] == false then
|
|
return true, false
|
|
end
|
|
end
|
|
return true, true
|
|
end
|
|
|
|
local function getProductInfo()
|
|
local success, result = nil, nil
|
|
if IsPurchasingConsumable then
|
|
success, result = pcall(function()
|
|
return MarketplaceService:GetProductInfo(PurchaseData.ProductId, Enum.InfoType.Product)
|
|
end)
|
|
else
|
|
success, result = pcall(function()
|
|
return MarketplaceService:GetProductInfo(PurchaseData.AssetId)
|
|
end)
|
|
end
|
|
|
|
if not success or not result then
|
|
print("PurchasePromptScript: getProductInfo failed because", result)
|
|
return nil
|
|
end
|
|
|
|
if type(result) ~= 'table' then
|
|
result = HttpService:JSONDecode(result)
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
-- returns success, doesOwnItem
|
|
local function doesPlayerOwnItem()
|
|
if not PurchaseData.AssetId or PurchaseData.AssetId <= 0 then
|
|
return false, nil
|
|
end
|
|
|
|
local success, result = pcall(function()
|
|
local apiPath = "ownership/hasAsset"
|
|
local params = "?userId="..tostring(Players.LocalPlayer.userId).."&assetId="..tostring(PurchaseData.AssetId)
|
|
return HttpRbxApiService:GetAsync(apiPath..params, true)
|
|
end)
|
|
|
|
if not success then
|
|
print("PurchasePromptScript: doesPlayerOwnItem() failed because", result)
|
|
return false, nil
|
|
end
|
|
|
|
if result == true or result == "true" then
|
|
return true, true
|
|
end
|
|
|
|
return true, false
|
|
end
|
|
|
|
local function isFreeItem()
|
|
return PurchaseData.ProductInfo and PurchaseData.ProductInfo["IsPublicDomain"] == true
|
|
end
|
|
|
|
local function getPlayerBalance()
|
|
local success, result = pcall(function()
|
|
local apiPath = "currency/balance"
|
|
return HttpRbxApiService:GetAsync(apiPath, true)
|
|
end)
|
|
|
|
if not success then
|
|
print("PurchasePromptScript: getPlayerBalance() failed because", result)
|
|
return nil
|
|
end
|
|
|
|
if result == '' then return end
|
|
|
|
return HttpService:JSONDecode(result)
|
|
end
|
|
|
|
local function isNotForSale()
|
|
return PurchaseData.ProductInfo['IsForSale'] == false and PurchaseData.ProductInfo["IsPublicDomain"] == false
|
|
end
|
|
|
|
local function playerHasFundsForPurchase(playerBalance)
|
|
local currencyTypeStr = nil
|
|
if PurchaseData.CurrencyType == Enum.CurrencyType.Robux then
|
|
currencyTypeStr = "robux"
|
|
elseif PurchaseData.CurrencyType == Enum.CurrencyType.Tix then
|
|
currencyTypeStr = "tickets"
|
|
else
|
|
return false
|
|
end
|
|
|
|
local playerBalanceInt = tonumber(playerBalance[currencyTypeStr])
|
|
if not playerBalanceInt then
|
|
return false
|
|
end
|
|
|
|
local afterBalanceAmount = playerBalanceInt - PurchaseData.CurrencyAmount
|
|
local currencyStr = getCurrencyString(PurchaseData.CurrencyType)
|
|
if afterBalanceAmount < 0 and PurchaseData.CurrencyType == Enum.CurrencyType.Robux then
|
|
PostBalanceText.Visible = false
|
|
return true, false
|
|
elseif afterBalanceAmount < 0 and PurchaseData.CurrencyType == Enum.CurrencyType.Tix then
|
|
PostBalanceText.Visible = true
|
|
PostBalanceText.Text = "You need "..formatNumber(-afterBalanceAmount).." more "..currencyStr.." to buy this item."
|
|
return true, false
|
|
end
|
|
|
|
if PurchaseData.CurrencyType == Enum.CurrencyType.Tix then
|
|
PostBalanceText.Text = PURCHASE_MSG.BALANCE_FUTURE..formatNumber(afterBalanceAmount).." "..currencyStr.."."
|
|
else
|
|
PostBalanceText.Text = PURCHASE_MSG.BALANCE_FUTURE..currencyStr..formatNumber(afterBalanceAmount).."."
|
|
end
|
|
return true, true
|
|
end
|
|
|
|
local function isUnder13()
|
|
if PurchaseData.ProductInfo["ContentRatingTypeId"] == 1 then
|
|
if Players.LocalPlayer:GetUnder13() then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function isLimitedUnique()
|
|
local productInfo = PurchaseData.ProductInfo
|
|
if productInfo then
|
|
if (productInfo["IsLimited"] or productInfo["IsLimitedUnique"]) and
|
|
(productInfo["Remaining"] == "" or productInfo["Remaining"] == 0 or productInfo["Remaining"] == nil or productInfo["Remaining"] == "null") then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- main validation function
|
|
local function canPurchase()
|
|
if isMarketplaceDown() then -- FFlag
|
|
onPurchaseFailed(PURCHASE_FAILED.IN_GAME_PURCHASE_DISABLED)
|
|
return false
|
|
end
|
|
|
|
if checkMarketplaceAvailable() then -- FFlag
|
|
local success, isAvailable = isMarketplaceAvailable()
|
|
if success then
|
|
if not isAvailable then
|
|
onPurchaseFailed(PURCHASE_FAILED.IN_GAME_PURCHASE_DISABLED)
|
|
return false
|
|
end
|
|
else
|
|
onPurchaseFailed(PURCHASE_FAILED.DEFAULT_ERROR)
|
|
return false
|
|
end
|
|
end
|
|
|
|
PurchaseData.ProductInfo = getProductInfo()
|
|
if not PurchaseData.ProductInfo then
|
|
onPurchaseFailed(PURCHASE_FAILED.IN_GAME_PURCHASE_DISABLED)
|
|
return false
|
|
end
|
|
|
|
if isNotForSale() then
|
|
onPurchaseFailed(PURCHASE_FAILED.NOT_FOR_SALE)
|
|
return false
|
|
end
|
|
|
|
-- check if owned by player; dev products are not owned
|
|
if not IsPurchasingConsumable then
|
|
local success, doesOwnItem = doesPlayerOwnItem()
|
|
if not success then
|
|
onPurchaseFailed(PURCHASE_FAILED.DEFAULT_ERROR)
|
|
return false
|
|
elseif doesOwnItem then
|
|
if not PurchaseData.ProductInfo then
|
|
onPurchaseFailed(PURCHASE_FAILED.DEFAULT_ERROR)
|
|
return false
|
|
end
|
|
setPreviewImage(PurchaseData.ProductInfo, PurchaseData.AssetId)
|
|
ItemDescriptionText.Text = PURCHASE_MSG.ALREADY_OWN
|
|
PostBalanceText.Visible = false
|
|
setButtonsVisible(OkButton)
|
|
return true
|
|
end
|
|
end
|
|
|
|
local isFree = isFreeItem()
|
|
|
|
local playerBalance = getPlayerBalance()
|
|
if not playerBalance then
|
|
onPurchaseFailed(PURCHASE_FAILED.CANNOT_GET_BALANCE)
|
|
return false
|
|
end
|
|
|
|
-- validate item price
|
|
setCurrencyData(playerBalance)
|
|
if not PurchaseData.CurrencyAmount and not isFree then
|
|
onPurchaseFailed(PURCHASE_FAILED.CANNOT_GET_ITEM_PRICE)
|
|
return false
|
|
end
|
|
|
|
-- check player funds
|
|
local hasFunds = nil
|
|
if not isFree then
|
|
local success = nil
|
|
success, hasFunds = playerHasFundsForPurchase(playerBalance)
|
|
if success then
|
|
if not hasFunds then
|
|
if PurchaseData.CurrencyType == Enum.CurrencyType.Tix then
|
|
onPurchaseFailed(PURCHASE_FAILED.NOT_ENOUGH_TIX)
|
|
return false
|
|
else
|
|
setBuyMoreRobuxDialog(playerBalance)
|
|
end
|
|
end
|
|
else
|
|
onPurchaseFailed(PURCHASE_FAILED.CANNOT_GET_BALANCE)
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- check membership type
|
|
local invalidBCLevel = PurchaseData.ProductInfo["MinimumMembershipLevel"] > Players.LocalPlayer.MembershipType.Value
|
|
|
|
-- check under 13
|
|
if isUnder13() then
|
|
onPurchaseFailed(PURCHASE_FAILED.UNDER_13)
|
|
return false
|
|
end
|
|
|
|
if isLimitedUnique() then
|
|
onPurchaseFailed(PURCHASE_FAILED.LIMITED)
|
|
return false
|
|
end
|
|
|
|
if (hasFunds or isFree or invalidBCLevel) then
|
|
if not setPurchaseDataInGui(isFree, invalidBCLevel) then
|
|
onPurchaseFailed(PURCHASE_FAILED.DEFAULT_ERROR)
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--[[ Purchase Functions ]]--
|
|
local function getToolAsset(assetId)
|
|
local tool = InsertService:LoadAsset(assetId)
|
|
if not tool then return nil end
|
|
--
|
|
if tool:IsA("Tool") then
|
|
return tool
|
|
end
|
|
|
|
local children = tool:GetChildren()
|
|
for i = 1, #children do
|
|
if children[i]:IsA("Tool") then
|
|
return children[i]
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onPurchaseSuccess()
|
|
IsCheckingPlayerFunds = false
|
|
local descriptionText = PURCHASE_MSG.SUCCEEDED
|
|
|
|
descriptionText = string.gsub(descriptionText, "itemName", string.sub(PurchaseData.ProductInfo["Name"], 1, 20))
|
|
ItemDescriptionText.Text = descriptionText
|
|
|
|
local playerBalance = getPlayerBalance()
|
|
local currencyType = PurchaseData.CurrencyType == Enum.CurrencyType.Tix and "tickets" or "robux"
|
|
local newBalance = playerBalance[currencyType]
|
|
|
|
if currencyType == "robux" then
|
|
PostBalanceText.Text = PURCHASE_MSG.BALANCE_NOW..getCurrencyString(PurchaseData.CurrencyType)..formatNumber(newBalance).."."
|
|
else
|
|
PostBalanceText.Text = PURCHASE_MSG.BALANCE_NOW..formatNumber(newBalance).." "..getCurrencyString(PurchaseData.CurrencyType).."."
|
|
end
|
|
|
|
if isFreeItem() then PostBalanceText.Visible = false end
|
|
|
|
setButtonsVisible(OkPurchasedButton)
|
|
stopPurchaseAnimation()
|
|
end
|
|
|
|
local function onAcceptPurchase()
|
|
if IsCurrentlyPurchasing then return end
|
|
--
|
|
IsCurrentlyPurchasing = true
|
|
startPurchaseAnimation()
|
|
local startTime = tick()
|
|
local apiPath = nil
|
|
local params = nil
|
|
local currencyTypeInt = nil
|
|
if PurchaseData.CurrencyType == Enum.CurrencyType.Robux or PurchaseData.CurrencyType == Enum.CurrencyType.Default then
|
|
currencyTypeInt = 1
|
|
elseif PurchaseData.CurrencyType == Enum.CurrencyType.Tix then
|
|
currencyTypeInt = 2
|
|
end
|
|
|
|
local productId = PurchaseData.ProductInfo["ProductId"]
|
|
if IsPurchasingConsumable then
|
|
apiPath = "marketplace/submitpurchase"
|
|
params = "productId="..tostring(productId).."¤cyTypeId="..tostring(currencyTypeInt)..
|
|
"&expectedUnitPrice="..tostring(PurchaseData.CurrencyAmount).."&placeId="..tostring(game.PlaceId)
|
|
params = params.."&requestId="..HttpService:UrlEncode(HttpService:GenerateGUID(false))
|
|
else
|
|
apiPath = "marketplace/purchase"
|
|
params = "productId="..tostring(productId).."¤cyTypeId="..tostring(currencyTypeInt)..
|
|
"&purchasePrice="..tostring(PurchaseData.CurrencyAmount or 0).."&locationType=Game&locationId="..tostring(game.PlaceId)
|
|
end
|
|
|
|
local success, result = pcall(function()
|
|
return HttpRbxApiService:PostAsync(apiPath, params, true, Enum.ThrottlingPriority.Default, Enum.HttpContentType.ApplicationUrlEncoded)
|
|
end)
|
|
|
|
-- retry
|
|
if IsPurchasingConsumable then
|
|
local retries = 3
|
|
local wasSuccess = success and result and result ~= ''
|
|
while retries > 0 and not wasSuccess do
|
|
wait(1)
|
|
retries = retries - 1
|
|
success, result = pcall(function()
|
|
return HttpRbxApiService:PostAsync(apiPath, params, true, Enum.ThrottlingPriority.Default, Enum.HttpContentType.ApplicationUrlEncoded)
|
|
end)
|
|
wasSuccess = success and result and result ~= ''
|
|
end
|
|
--
|
|
game:ReportInGoogleAnalytics("Developer Product", "Purchase",
|
|
wasSuccess and ("success. Retries = "..(3 - retries)) or ("failure: " .. tostring(result)), 1)
|
|
end
|
|
|
|
if tick() - startTime < 1 then wait(1) end -- artifical delay to show spinner for at least 1 second
|
|
|
|
if not success then
|
|
print("PurchasePromptScript: onAcceptPurchase() failed because", result)
|
|
onPurchaseFailed(PURCHASE_FAILED.DEFAULT_ERROR)
|
|
return
|
|
end
|
|
|
|
result = HttpService:JSONDecode(result)
|
|
if result then
|
|
if result["success"] == false then
|
|
if result["status"] ~= "AlreadyOwned" then
|
|
print("PurchasePromptScript: onAcceptPurchase() response failed because", tostring(result["status"]))
|
|
if result["status"] == "EconomyDisabled" then
|
|
onPurchaseFailed(PURCHASE_FAILED.IN_GAME_PURCHASE_DISABLED)
|
|
else
|
|
onPurchaseFailed(PURCHASE_FAILED.DEFAULT_ERROR)
|
|
end
|
|
return
|
|
end
|
|
end
|
|
else
|
|
print("PurchasePromptScript: onAcceptPurchase() failed to parse JSON of", productId)
|
|
onPurchaseFailed(PURCHASE_FAILED.DEFAULT_ERROR)
|
|
return
|
|
end
|
|
|
|
if PurchaseData.EquipOnPurchase and PurchaseData.AssetId and tonumber(PurchaseData.ProductInfo["AssetTypeId"]) == 19 then
|
|
local tool = getToolAsset(tonumber(PurchaseData.AssetId))
|
|
if tool then
|
|
tool.Parent = Players.LocalPlayer.Backpack
|
|
end
|
|
end
|
|
|
|
if IsPurchasingConsumable then
|
|
if not result["receipt"] then
|
|
print("PurchasePromptScript: onAcceptPurchase() failed because no dev product receipt was returned for", tostring(productId))
|
|
onPurchaseFailed(PURCHASE_FAILED.DEFAULT_ERROR)
|
|
return
|
|
end
|
|
MarketplaceService:SignalClientPurchaseSuccess(tostring(result["receipt"]), Players.LocalPlayer.userId, productId)
|
|
else
|
|
onPurchaseSuccess()
|
|
end
|
|
end
|
|
|
|
-- main entry point
|
|
local function onPurchasePrompt(player, assetId, equipIfPurchased, currencyType, productId)
|
|
if player == Players.LocalPlayer and not IsCurrentlyPrompting then
|
|
IsCurrentlyPrompting = true
|
|
setInitialPurchaseData(assetId, productId, currencyType, equipIfPurchased)
|
|
if canPurchase() then
|
|
showPurchasePrompt()
|
|
end
|
|
end
|
|
end
|
|
|
|
local function onBuyRobuxPrompt()
|
|
startPurchaseAnimation()
|
|
if IsNativePurchasing then
|
|
MarketplaceService:PromptNativePurchase(Players.LocalPlayer, ThirdPartyProductName)
|
|
else
|
|
IsCheckingPlayerFunds = true
|
|
GuiService:OpenBrowserWindow(BASE_URL.."Upgrades/Robux.aspx")
|
|
end
|
|
end
|
|
|
|
local function onUpgradeBCPrompt()
|
|
IsCheckingPlayerFunds = true
|
|
GuiService:OpenBrowserWindow(BASE_URL.."Upgrades/BuildersClubMemberships.aspx")
|
|
end
|
|
|
|
--[[ Event Connections ]]--
|
|
CancelButton.MouseButton1Click:connect(function()
|
|
if IsCurrentlyPurchasing then return end
|
|
onPromptEnded(false)
|
|
end)
|
|
BuyButton.MouseButton1Click:connect(onAcceptPurchase)
|
|
FreeButton.MouseButton1Click:connect(onAcceptPurchase)
|
|
OkButton.MouseButton1Click:connect(function()
|
|
onPromptEnded(false)
|
|
end)
|
|
OkPurchasedButton.MouseButton1Click:connect(function()
|
|
onPromptEnded(true)
|
|
end)
|
|
BuyRobuxButton.MouseButton1Click:connect(onBuyRobuxPrompt)
|
|
BuyBCButton.MouseButton1Click:connect(onUpgradeBCPrompt)
|
|
|
|
MarketplaceService.PromptProductPurchaseRequested:connect(function(player, productId, equipIfPurchased, currencyType)
|
|
onPurchasePrompt(player, nil, equipIfPurchased, currencyType, productId)
|
|
end)
|
|
MarketplaceService.PromptPurchaseRequested:connect(function(player, assetId, equipIfPurchased, currencyType)
|
|
onPurchasePrompt(player, assetId, equipIfPurchased, currencyType, nil)
|
|
end)
|
|
MarketplaceService.ServerPurchaseVerification:connect(function(serverResponseTable)
|
|
if not serverResponseTable then
|
|
onPurchaseFailed(PURCHASE_FAILED.DEFAULT_ERROR)
|
|
return
|
|
end
|
|
|
|
if serverResponseTable["playerId"] and tonumber(serverResponseTable["playerId"]) == Players.LocalPlayer.userId then
|
|
onPurchaseSuccess()
|
|
end
|
|
end)
|
|
|
|
local function retryPurchase()
|
|
local canMakePurchase = canPurchase()
|
|
if not canMakePurchase then
|
|
local retries = 20
|
|
while retries > 0 and not canMakePurchase do
|
|
wait(0.5)
|
|
canMakePurchase = canPurchase()
|
|
retries = retries - 1
|
|
end
|
|
end
|
|
end
|
|
|
|
GuiService.BrowserWindowClosed:connect(function()
|
|
if IsCheckingPlayerFunds then
|
|
retryPurchase()
|
|
end
|
|
stopPurchaseAnimation()
|
|
end)
|
|
|
|
if IsNativePurchasing then
|
|
MarketplaceService.NativePurchaseFinished:connect(function(player, productId, wasPurchased)
|
|
if wasPurchased then
|
|
retryPurchase()
|
|
else
|
|
onPurchaseFailed(PURCHASE_FAILED.DID_NOT_BUY_ROBUX)
|
|
end
|
|
stopPurchaseAnimation()
|
|
end)
|
|
end
|