Agency Docs
QBESXStandalone

Agency-Phone Кастомные Приложения

Теперь с AgencyOS27: бесплатное обновление для всех, у кого уже есть телефон.

v27.1.2Платно8 Страницы

🧩 Своё приложение для Agency-Phone

Добавить приложение в телефон можно двумя способами. Выберите подходящий:

СпособПодходит дляЧто вы меняете
A. SDK (внешний ресурс)Разработчиков и скриптов, которые добавляют приложение со своей иконкойТолько свой ресурс. Никаких правок в agency-phone.
B. Drop-in (внутри agency-phone)Владельцев серверов, которые добавляют нативный экран в интерфейс телефонаconfig.lua + apps/ (+ html/js для экрана)

Если вы распространяете приложение на другие серверы, используйте способ A. Ему не нужен наш исходный код, и он продолжает работать после обновлений.

A SDK: регистрация приложения из своего ресурса

agency-phone предоставляет экспорты, с помощью которых любой ресурс во время работы добавляет приложение со своей иконкой на главный экран телефона. Интерфейс приложения живёт в вашем ресурсе; телефон показывает иконку и передаёт вам управление, когда игрок на неё нажимает. Без правок в agency-phone и без разблокированной сборки.

Минимальный пример

-- client side of YOUR resource
CreateThread(function()
    Wait(1500) -- let agency-phone boot
    exports['agency-phone']:RegisterApp({
        id        = 'myshop',
        label     = 'My Shop',
        icon      = 'fa-solid fa-cart-shopping', -- FontAwesome 6 class
        -- image  = 'https://cfx-nui-myresource/icon.png', -- or your own image instead
        color     = '#ff9500',
        colorDark = '#e08600',
    })
end)

-- fired when the player taps your icon on the phone
AddEventHandler('agency-phone:client:openApp', function(appId)
    if appId ~= 'myshop' then return end
    exports['agency-phone']:ClosePhone()
    -- open your own NUI here
end)

Параметры RegisterApp

ПолеТипОбязательноОписание
idstringдаУникальный id, строчными буквами, без пробелов.
labelstringдаНазвание под иконкой.
iconstringнетКласс FontAwesome 6. Игнорируется, если задан image.
imagestringнетСвоя картинка иконки, например https://cfx-nui-<resource>/icon.png.
color / colorDarkstringнетГрадиент за иконкой (hex).
slotnumberнетПозиция на главном экране. Если не указана, выбирается автоматически.
storeDescstringнетКраткое описание на странице приложения в Store. Если не указано, используется label.

Экспорты SDK (клиент)

ЭкспортОписание
RegisterApp(config)Добавить или обновить приложение. Возвращает true или false, err.
UnregisterApp(id)Удалить приложение. Происходит и автоматически, когда ваш ресурс останавливается.
GetRegisteredApps()Таблица всех зарегистрированных внешних приложений.
ClosePhone()Закрыть телефон, например перед открытием своего NUI.

Событие нажатия

Когда игрок нажимает на вашу иконку, agency-phone вызывает на клиенте оба события. Используйте любое:

СобытиеАргументы
agency-phone:client:openAppappId (фильтруйте по своему id)
agency-phone:client:openApp:<yourId>нет (уже относится только к вашему приложению)

Добавьте dependency 'agency-phone' в свой fxmanifest.lua, чтобы порядок запуска был правильным.

B Drop-in, нативный экран внутри телефона

Для экрана, который рисует сам телефон (а не ваш ресурс). При этом меняются файлы agency-phone.

ШагФайлМожно менять в escrow-сборке?
1. Запись приложения в AgencyConfig.Appsconfig.lua✓ да
2. Логика RegisterNUICallbackapps/<id>_client.lua (+ _server.lua)✓ да
3. Экран <div id="app-<id>">html/index.html✗ зашифрован
4. case '<id>': в openApp() + загрузчикhtml/js/phone.js✗ зашифрован

Шаги 3 и 4 находятся в файлах, зашифрованных escrow, поэтому нативному экрану нужна сборка с исходниками или разблокированная сборка. Если у вас только escrow-версия, используйте способ A (экран живёт в вашем ресурсе). Примеры внутри ресурса: apps/testapp_client.lua и apps/dispatch_client.lua.

📋 Другие экспорты телефона

Клиент

ЭкспортВозвращаетОписание
IsPhoneOpen()booleanТелефон сейчас открыт.
IsPhonePoweredOn()booleanУстройство включено.
HasPhone()booleanУ игрока есть предмет телефона.
HasCarrierContract()booleanАктивная eSIM или контракт.
GetPhoneData()tableСнимок состояния телефона.
PhoneNotification(title, text, icon, color, timeout, acceptIcon, denyIcon)voidУведомление на телефоне.
StartCall(contact)voidЗвонок на { name, number }.
StartAgencyPayCheckout(opts)voidОплата AgencyPay (см. ниже).

Сервер

ЭкспортОписание
SendMail(identifier, mailData)Доставить письмо игроку (онлайн или офлайн).
AddChirp(data)Опубликовать сообщение в соцленте Pulse.
VerifyAgencyPayCharge(source, requestId, minAmount)Подтвердить на сервере оплаченный счёт AgencyPay перед выдачей товара (см. ниже).

⇄ Мост NUI ↔ Lua (способ B)

НаправлениеСторона JSСторона Lua
JS → LuafetchNUI('callbackName', data)RegisterNUICallback('callbackName', fn)
Lua → JSслушайте action в window.onmessageSendNUIMessage({ action = '...' })

💳 Оплата AgencyPay

Списывайте деньги с кошелька игрока из любого ресурса.

exports['agency-phone']:StartAgencyPayCheckout({
    merchant = 'My Shop', title = 'Agency Pay', subtitle = 'Checkout',
    label = 'Premium Coffee', amount = 24.50,
    icon = 'fa-solid fa-mug-hot', accent = '#ff9f0a',
    clientEvent = 'myshop:client:agencyPayResult',
    serverEvent = 'myshop:server:agencyPayResult',
    metadata = { orderId = 'order_2048' },
})

RegisterNetEvent('myshop:client:agencyPayResult', function(result)
    if result.status == 'paid' then
        -- deliver the goods
    end
end)

Результат содержит status (paid / cancelled / failed), amount, cardId, balance и ваши metadata.

Результат paid, пришедший от клиента в ваш ресурс, сам по себе ничего не доказывает. Передайте свой id в StartAgencyPayCheckout и перед выдачей чего-либо вызовите на сервере exports['agency-phone']:VerifyAgencyPayCharge(source, id, amount). Он возвращает true и сумму, только если этот игрок оплатил этот счёт за последние 60 секунд, и списывает платёж, чтобы один и тот же платёж нельзя было засчитать дважды.

🐛 Решение проблем

СимптомРешение
Иконка не появляется (SDK)Обновите agency-phone до последней сборки Keymaster, добавьте Wait(1500) перед RegisterApp и dependency 'agency-phone' в манифест.
Нажатие ничего не делает (SDK)Ваш обработчик agency-phone:client:openApp должен точно совпадать с вашим id (с учётом регистра).
Своя картинка не отображаетсяИспользуйте полный NUI-адрес https://cfx-nui-<your-resource>/path/icon.png и перечислите файл в files { } манифеста.
Иконка не появляется (drop-in)enabled = true и installed = true в записи конфига.

Есть вопросы или нужна помощь? Заходите в Discord Agency Scripts.

После этой страницы всё ещё не получается? Дальше подключается наша поддержка.