Разработка игрLua / Luau

Как сделать кнопку доната в Roblox Studio: покупка за Robux

Создаём Developer Product и кнопку доната в Roblox Studio: PromptProductPurchase на клиенте, ProcessReceipt на сервере, выдача награды и тест.

Кодик

Автор

5 мин чтения

Кнопка доната только открывает системное окно покупки. Награду выдаёт только сервер через ProcessReceipt. Так клиент не может подделать успешную оплату, а необработанная квитанция может быть повторена.

Developer Product подходит для повторяемой покупки: монет, пополнения здоровья или добровольной поддержки. Для постоянного предмета, который покупают один раз, нужен Pass. В этой статье наградой будут 100 монет, чтобы весь цикл можно было проверить по leaderstats.

1Просим

LocalScript вызывает PromptProductPurchase с нужным productId.

2Проверяем

Roblox передаёт квитанцию в серверный ProcessReceipt.

3Выдаём

Сервер находит игрока, добавляет монеты и подтверждает обработку.

Чем Developer Product отличается от Pass

Сначала опубликуйте experience, затем в Creator Hub откройте Monetization и Developer Products. Создайте продукт и скопируйте его ID, не ID места и не ID картинки. Кнопка лежит в ScreenGui, LocalScript рядом с ней, а обработчик квитанций в ServerScriptService.

ПокупкаТипКак часто
100 монетDeveloper ProductМногократно
Постоянный VIPPassОдин раз
Доступ на месяцSubscriptionПо периоду
Добровольная поддержкаDeveloper ProductМногократно

После этого шага у каждой части есть одна роль. Интерфейс собирает действие, логика меняет состояние, а вывод показывает признак успеха. Это проще отлаживать, чем один большой обработчик.

Путь одной покупки
Клиент просит, сервер выдаёт. PurchaseGranted возвращается после выдачи

Сначала соберите минимальный сценарий. Не добавляйте оформление, хранение и дополнительные режимы одновременно. Добейтесь одного правильного прохода от входных данных до результата, а затем проверяйте каждое улучшение отдельно.

Пишем клиентскую кнопку и ProcessReceipt

Две половины нельзя склеивать в один LocalScript. Клиент знает только productId и открывает покупку. Сервер принимает receiptInfo от Roblox, проверяет ProductId и только после успешной выдачи возвращает PurchaseGranted.

-- LocalScript в TextButton
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local PRODUCT_ID = 123456789 -- замените на свой ID

script.Parent.Activated:Connect(function()
    MarketplaceService:PromptProductPurchase(Players.LocalPlayer, PRODUCT_ID)
end)

-- Script в ServerScriptService
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local PRODUCT_ID = 123456789
local COINS_PER_PURCHASE = 100

MarketplaceService.ProcessReceipt = function(receiptInfo)
    if receiptInfo.ProductId ~= PRODUCT_ID then
        return Enum.ProductPurchaseDecision.NotProcessedYet
    end

    local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
    if not player then
        return Enum.ProductPurchaseDecision.NotProcessedYet
    end

    local leaderstats = player:FindFirstChild("leaderstats")
    local coins = leaderstats and leaderstats:FindFirstChild("Coins")
    if not coins then
        return Enum.ProductPurchaseDecision.NotProcessedYet
    end

    coins.Value += COINS_PER_PURCHASE
    return Enum.ProductPurchaseDecision.PurchaseGranted
end
Что должно работать после первого запуска

Клиент не выдаёт себе монеты. Незнакомый ProductId не подтверждается. Отсутствующий игрок или Coins дают Roblox повторить квитанцию. Подтверждение возвращается только после изменения Coins.

Клиент и сервер
Граница доверия в покупке. Сравнение показывает, какой вариант легче поддерживать и проверять.

Почему нельзя верить PromptProductPurchaseFinished

PromptProductPurchaseFinished сообщает о закрытии диалога, но не является надёжным доказательством оплаты. Официальная схема требует ProcessReceipt на сервере. Одна и та же квитанция может прийти повторно, поэтому в продакшене её связывают с PurchaseId и хранят факт выдачи атомарно.

СигналМожно доверятьРоль
Клик по кнопкеНетТолько открывает диалог
PromptProductPurchaseFinishedНетНе выдаёт награду
ProcessReceiptДаПроверяет квитанцию на сервере
PurchaseGrantedДа, после выдачиЗакрывает обработку

Сохраняйте в одном месте то, что может измениться. Если одно и то же значение скопировано в три функции, после первой правки они начнут расходиться. Один объект состояния и одна функция отрисовки снимают эту проблему.

Смотрите на состояние, а не только на красивый экран. Перезагрузка, повторный клик, пустой ввод и медленный ответ часто открывают ошибки, которые не видны в идеальном демонстрационном проходе.

Практика: тестируем весь путь покупки

Покупки в тестовом режиме могут стоить реальные Robux, поэтому используйте недорогой продукт и проверяйте актуальные условия Creator Hub. До теста добавьте leaderstats и Coins, иначе квитанция будет откладываться. Смотрите Output и значение Coins на сервере, а не рисуйте фальшивую галочку на клиенте.

Проверка своими руками
  1. Опубликуйте experience и создайте Developer Product.
  2. Замените 123456789 в обоих частях кода на свой productId.
  3. Запустите игру и нажмите кнопку один раз.
  4. Отмените диалог и проверьте, что Coins не изменились.
  5. Проведите одну контролируемую покупку и сверьте прибавку.
  6. Перезайдите в игру и убедитесь, что отложенные квитанции не теряются.
Готово, если выполняются все пункты

Кнопка открывает системный диалог с верным продуктом. Отмена не меняет Coins. Успешная покупка даёт ровно 100 монет. Сервер возвращает NotProcessedYet, если не смог выдать награду.

Четыре точки проверки
До того, как кнопку увидят игроки. Пройдите все четыре сценария перед публикацией проекта.

Доводим кнопку доната до готового вида

Не прячьте цену и не подменяйте системное окно своим. Кнопка должна называть награду и давать понять, что покупка повторяется. Если это именно поддержка автора, награду можно сделать небольшой и не влияющей на баланс. На мобильном экране не ставьте кнопку поверх штатных элементов управления.

Монеты выдаёт LocalScript

Клиенту нельзя доверять результат покупки.

Награда по PromptProductPurchaseFinished

Это событие не заменяет ProcessReceipt.

PurchaseGranted до выдачи

Квитанция закроется, а игрок может не получить покупку.

Один ProcessReceipt в нескольких Script

Обработчик должен быть один и маршрутизировать ProductId.

Сверьтесь с первичным источником. Поведение используемых функций и ограничения примера проверяйте по документацией Roblox по Developer Products. Документация особенно важна, когда меняются версии, политика платформы или формат ответа API.

Что получится в итоге

Готовый результат проекта: Как сделать кнопку доната в Roblox Studio: покупка за Robux
В готовом интерфейсе игрок видит награду и кнопку, а после оплаты сервер начисляет монеты.

Короткие ответы
Когда нужен Developer Product?

Когда один и тот же товар можно покупать многократно.

Где выдавать награду?

Только на сервере в ProcessReceipt после проверки receiptInfo.

Что вернуть, если Coins ещё нет?

NotProcessedYet, чтобы Roblox повторил квитанцию позже.

Как понять, что проект готов?

Он повторяет целевой сценарий несколько раз подряд, понятно реагирует на ошибочный ввод и не теряет состояние при обычных действиях.

Можно ли копировать код целиком?

Можно как стартовую точку, но сначала замените значения из примера, затем запустите проект по шагам. Если вы не можете объяснить одну строку, её лучше разобрать до следующего шага.

Привяжите покупку к игровой ценности

Клиентские и серверные скрипты разбираем в курсе Lua. Кнопку можно встроить в магазин Roblox.

Для счёта монет пригодится таблица leaderstats, а для вечного результата нужно DataStore. В Кодике можно отдельно прогнать каждую функцию до переноса в Studio.