> For the complete documentation index, see [llms.txt](https://feron-scripts.gitbook.io/welcome/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://feron-scripts.gitbook.io/welcome/scripts/vip-system-v2.md).

# Feron VIP System V2

In-game donation / VIP store for FiveM. Players spend a server credit (default **VC — VIP Coin**) on vehicles, weapons, money, bundles, timed VIP memberships, character slots, houses and customization services. Real-money payments stay on **Tebex**; the store turns Tebex purchases into one-time redeem codes.

* **Resource name:** `fivem_vip_v2`
* **Frameworks:** QBCore / Qbox (QB package, auto-detected) · ESX Legacy (ESX package)
* **MySQL:** oxmysql (recommended) / mysql-async — auto-detected via `Config.Mysql`
* **Editions:** Escrowed or Open Source (Open Source includes the Vue 3 UI source)

{% hint style="info" %}
The QB package contains no ESX code and the ESX package contains no QB code. The folder inside every zip is `fivem_vip_v2`, so `ensure`, exports and the database stay the same if you switch packages.
{% endhint %}

## Features

* Store categories: Vehicles · Air · Weapons · Bundles · VIP · Money · Customize · Houses
* **Atomic credit economy** — spending is a single conditional `UPDATE`, balance can never go negative; every movement is written to a ledger
* **Server-side pricing** — the price sent by the UI is never trusted
* **Automatic refund** — if delivery fails (or throws), credits are returned and a `refund` entry is logged; bundles are all-or-nothing
* **Timed VIP memberships** — buying again *extends* the time, perks: connectqueue priority, `IsVip` export, members-only products (shown locked, not hidden)
* **Daily login reward** — a fixed, visible 7-day calendar with a larger VIP calendar (no randomness)
* **Supply Drops** — bundles with fixed, listed contents (no loot boxes, see [Cfx policy](#cfx-policy-notes))
* **Test drive** — try a vehicle before buying; server-spawned, temporary, auto-deleted
* **Upsells** — custom plate at checkout, plate change and phone number change products
* **Character slot product** (requires `feron_multicharacter`)
* **House product** — built-in `qb-houses` support, any other housing script via `Config.Housing`
* Gift codes, Tebex redeem codes, deals of the day with daily reset
* Discord webhook logging — purchases, refunds, codes, Tebex, manual credit changes
* Custom product types without touching the core (`bridge/custom_types.lua`)
* Rebrandable (`Config.Brand`), locales: English · Turkish
* One-time automatic migration from V1 (`dockerize_vip`) data

## Supported integrations

| Area            | Supported                                                                                                 | Config key           |
| --------------- | --------------------------------------------------------------------------------------------------------- | -------------------- |
| Framework       | `qb-core`, `qbx_core`, `es_extended`                                                                      | `Config.Framework`   |
| Inventory       | `ox_inventory`, `qs-inventory`, `ps-inventory`, `codem-inventory` (falls back to the framework inventory) | `Config.Inventory`   |
| Notifications   | `ox_lib`, `okokNotify` (falls back to the framework notify)                                               | `Config.Notify`      |
| Queue priority  | `connectqueue` (via exports, no `server_script` include needed)                                           | `Config.Memberships` |
| Character slots | `feron_multicharacter`                                                                                    | —                    |
| Housing         | `qb-houses` built in; any script via export / event / function                                            | `Config.Housing`     |

If an optional integration is missing, the related product simply can't be bought (the pre-purchase check blocks it) — nothing breaks.

## Installation

### 1. Drop the resource

Copy `fivem_vip_v2/` into your `resources/` folder.

### 2. `server.cfg`

```cfg
ensure oxmysql
ensure fivem_vip_v2
```

Start your framework and database resource before `fivem_vip_v2`. **OneSync** is required for the test drive (it is on by default).

### 3. Database

Nothing to import. All tables are created automatically on first start (`CREATE TABLE IF NOT EXISTS`):

| Table                           | Purpose                                   |
| ------------------------------- | ----------------------------------------- |
| `dm_accounts`                   | Credit balance per player                 |
| `dm_transactions`               | Ledger of every credit movement           |
| `dm_purchases`                  | Purchase history                          |
| `dm_codes`                      | Gift codes and Tebex redeem codes         |
| `dm_categories` / `dm_products` | Store catalog                             |
| `dm_memberships`                | VIP memberships and expiry dates          |
| `dm_daily`                      | Daily reward streaks                      |
| `dm_meta`                       | Internal flags (seed / migration markers) |

On the first start the catalog in `config/products.lua` is imported into the database. After that the **database is the source of truth** — see [Config](/welcome/scripts/vip-system-v2/config.md).

If V1 (`dockerize_vip`) tables exist, balances and redeem codes are migrated once (`Config.MigrateFromV1`).

### 4. Edit the config

Edit `config/config.lua` (behaviour) and `config/products.lua` (seed catalog). Full reference: [Config](/welcome/scripts/vip-system-v2/config.md).

***

## Tebex setup

Paste this into your Tebex package's **Commands** field and set `credit` to the amount the package grants:

```
purchase_vip_credit_tebex {"transid":"{transaction}","credit":500}
```

When a player buys:

1. Tebex runs the command on your server console
2. A single-use redeem code is created — the code is the Tebex transaction ID
3. The player opens the store → **Credits** page → enters the code and receives the credits

The same transaction can never be redeemed twice. The command is **console-only** — it can't be run from in-game, even by admins.

{% hint style="warning" %}
**Upgrading from V1?** The command name and payload format must match the line above — update the command in your Tebex packages.
{% endhint %}

The cards on the Credits page come from `Config.CreditPacks`; each card's `url` should point to the matching Tebex package.

## Commands

| Command                             | Who          | Description                                                  |
| ----------------------------------- | ------------ | ------------------------------------------------------------ |
| `/donate`                           | Everyone     | Opens the store (`Config.MenuCommand`)                       |
| `/endtestdrive`                     | Everyone     | Ends a running test drive (`Config.TestDrive.cancelCommand`) |
| `/add_vip_credits <id> <amount>`    | Admin        | Gives credits to a player                                    |
| `/remove_vip_credits <id> <amount>` | Admin        | Removes credits (never below 0)                              |
| `/create_vip_code <credits> [uses]` | Admin        | Creates a gift code                                          |
| `/reload_vip_market`                | Admin        | Reloads the catalog from the database                        |
| `purchase_vip_credit_tebex <json>`  | Console only | Turns a Tebex purchase into a redeem code                    |

### Permissions

Admin commands are registered as **restricted**, so FiveM's own ACE system controls access — there is no separate group list in the config. Anyone with `group.admin` (the default `server.cfg` and txAdmin setups already grant `command allow`) can use them.

QBCore / Qbox staff are `qbcore.<rank>` principals, not `group.admin`. To let them use the commands, add a line:

```cfg
add_ace qbcore.admin command allow                    # all qb admins
add_ace group.mod command.add_vip_credits allow       # or one group, one command
```

A player's **job is never treated as a permission**.

## Exports

```lua
exports.fivem_vip_v2:GetCredits(src)                  --> number
exports.fivem_vip_v2:AddCredits(src, 500, 'reason')   --> new balance
exports.fivem_vip_v2:TakeCredits(src, 200, 'reason')  --> boolean

-- Membership
exports.fivem_vip_v2:IsVip(src)                       --> boolean
exports.fivem_vip_v2:IsVip(src, 'vip')                --> specific tier
exports.fivem_vip_v2:GetMembership(src)               --> { tier, label, expiresAt, daysLeft, active } | nil
exports.fivem_vip_v2:GrantMembership(src, 'vip', 30)  --> grant without charging credits

-- Test drive
exports.fivem_vip_v2:IsTestDriving(src)               --> boolean
```

`GetMembership` also returns expired memberships (`active = false`).

## Test drive

The **Test drive** button on a vehicle card teleports the player to the test area (`Config.TestDrive.area`, default: LSIA runway), seats them in the vehicle and returns them to where they started when time runs out or they use `/endtestdrive`.

* The vehicle is created **server-side**, never written to a garage, uses a temporary plate and is deleted when the session ends: timer, cancel, death, leaving the vehicle, disconnect or resource stop
* One test drive per player at a time
* The test vehicle is networked — if your test area is public, other players will see it. Move the area somewhere quiet if needed
* **Add-on vehicles** that are not helicopters/planes/boats etc. work as is; for add-on aircraft, boats or bikes set `payload.vehicleType` on the product (see [Config](/welcome/scripts/vip-system-v2/config.md))

## Custom product types

Add your own product type in `bridge/custom_types.lua` (not escrowed):

```lua
DM.RegisterProductType('mooring', {
  validate = function(p) return p.payload.dockId ~= nil, 'payload.dockId missing' end,
  deliver  = function(src, p) return exports['my-marina']:Reserve(src, p.payload.dockId) end,
  preview  = 'item',
})
```

`deliver` must return `true`; anything else refunds the player. Don't re-register built-in type names (such as `house`) — to support another housing script, use `Config.Housing` instead.

**Phone number change:** the `phone_change` product fires the server event `dm:phoneChange (src, newNumber)` — hook it (or override the type in `bridge/custom_types.lua`) to write the number into your phone script.

## Cfx policy notes

What you may sell is defined by the Cfx **Creator Platform License Agreement** (`fivem.net/terms`), not by this script. The default catalog was built with it in mind:

* **No loot boxes / crates** — chance-based mechanics are prohibited even for in-game currency. Supply Drops with fixed, listed contents are offered instead, and the daily reward is a fixed calendar
* **Memberships and character slots** — subscription / service products
* **Money, vehicles and weapons** are included because the market expects them, but selling in-game currency or Rockstar-owned items may fall under the agreement's prohibited methods — **removing them is the server owner's decision**
* **Payments only through Tebex**

Always check the current agreement before going live.

## Known limitations

* Stock is locked per player: two players buying the last `stock = 1` item at the exact same moment may both succeed
* The **Season** page is a placeholder; there is no cart (stackable types have a quantity selector)
* No in-game admin panel yet — edit the catalog in the database or re-seed it

***

For the full config reference, see [Config](/welcome/scripts/vip-system-v2/config.md).
