Skip to content
RustPluginGenerator

CanPurchaseItem

CanPurchaseItem is called from VendingMachine.DoTransaction right before a sold item is handed to the buyer. Returning a non-null value stops the transaction path and controls the DoTransaction bool result.

Signature
object CanPurchaseItem(BasePlayer buyer, Item item, Action<BasePlayer, Item> onItemPurchased, VendingMachine vendingMachine, ItemContainer targetContainer)

Parameters

Hook parameters
ParameterTypeDescription
buyerBasePlayerThe BasePlayer who is performing the vending machine purchase. May be null-checked; treat it as possibly disconnected.
itemItemThe Item being sold by the vending machine, already built from the sell order. Its amount reflects how many units the buyer is receiving.
onItemPurchasedAction<BasePlayer, Item>A callback the game uses to deliver the sold item and run its post-purchase logic. Pass it along if your own logic forwards the purchase.
vendingMachineVendingMachineThe VendingMachine entity the buyer is using. Use it to inspect sell orders, position, stock or ownership.
targetContainerItemContainerThe ItemContainer the sold item would go into for the buyer. May be the buyer's main inventory or another container.

Return value

Returning null lets the sale continue unchanged. Returning true stops the original code path and makes DoTransaction return true. Returning false, or any other non-null object, stops the path and makes DoTransaction return false.

How it works

CanPurchaseItem fires inside VendingMachine.DoTransaction after the sell order has been resolved and the sold item (item) has been constructed. At that point buyer, vendingMachine and targetContainer are available, and onItemPurchased is the delegate the machine would use to finish delivery. The hook is the last gate before the bought item reaches the player, so it is the right place to enforce per-player limits, blacklist certain items, or block purchases when an external plugin is unavailable.

Return behavior is strict: any non-null return stops the original code path. If you return a bool, that value becomes DoTransaction's result; returning true reports a successful sale without the game continuing, and false reports failure. Returning any other non-null object also stops the path and reports false. Return null to let the purchase proceed normally.

Because the item and the callback are already prepared when the hook runs, declining the purchase does not refund anything by itself — the game's stock and currency were already touched earlier in DoTransaction. Use the hook to prevent delivery, then handle messaging and any compensation in your plugin. The hook fires for player-driven vending transactions only.

Example plugin: Block buying of blacklisted items from vending machines

VendingPurchaseBlacklist.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Vending Purchase Blacklist", "RustPluginGenerator", "1.0.0")]
    [Description("Blocks purchases of blacklisted items from vending machines.")]
    public class VendingPurchaseBlacklist : RustPlugin
    {
        private const string PermReload = "vendingpurchaseblacklist.reload";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Blocked item shortnames")]
            public List<string> BlockedItems = new List<string> { "scrap", "supply.signal" };

            [JsonProperty("Message on block")]
            public string BlockMessage = "You cannot buy that item from a vending machine.";
        }

        protected override void LoadDefaultConfig() => config = new PluginConfig();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null) throw new Exception();
            }
            catch
            {
                PrintWarning("Configuration file is invalid, using defaults");
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["Blocked"] = "You cannot buy {0} from a vending machine.",
                ["Reloaded"] = "Blacklist reloaded. {0} item(s) blocked.",
                ["NoPermission"] = "You do not have permission to use this command."
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args) =>
            string.Format(lang.GetMessage(key, this, userId), args);

        private void Init()
        {
            permission.RegisterPermission(PermReload, this);
        }

        private object CanPurchaseItem(BasePlayer buyer, Item item, Action<BasePlayer, Item> onItemPurchased, VendingMachine vendingMachine, ItemContainer targetContainer)
        {
            if (buyer == null || item == null || item.info == null || vendingMachine == null)
                return null;

            if (config.BlockedItems == null || config.BlockedItems.Count == 0)
                return null;

            string shortname = item.info.shortname;
            if (string.IsNullOrEmpty(shortname))
                return null;

            foreach (string blocked in config.BlockedItems)
            {
                if (blocked != null && blocked == shortname)
                {
                    buyer.ChatMessage(Msg("Blocked", buyer.UserIDString, item.info.displayName.english));
                    return true;
                }
            }

            return null;
        }

        [ChatCommand("vpb.reload")]
        private void CmdReload(BasePlayer player, string command, string[] args)
        {
            if (player != null && !permission.UserHasPermission(player.UserIDString, PermReload))
            {
                player.ChatMessage(Msg("NoPermission", player.UserIDString));
                return;
            }

            LoadConfig();
            int count = config.BlockedItems == null ? 0 : config.BlockedItems.Count;
            if (player != null) player.ChatMessage(Msg("Reloaded", player.UserIDString, count));
            else Puts("Blacklist reloaded. " + count + " item(s) blocked.");
        }
    }
}

The plugin keeps a configurable list of item shortnames that should never be bought from a vending machine. In CanPurchaseItem it null-checks the buyer, item and machine, then checks whether item.info.shortname (or the machine's short prefab name, when configured) is on the list. If so, it sends a localized warning to the buyer and returns true; because the queued onItemPurchased callback never runs, the item is not delivered, while the machine still reports the transaction as completed. Otherwise it returns null so vanilla behavior is untouched. The list lives in the config and reloads through /vpb.reload, which clears any cached shortnames.

Common use cases

  • Per-player purchase limits

    Track purchases per Steam ID in a data file and return true once a player exceeds a daily cap, then reset counters with a timer.

  • Server-wide item blacklist

    Return true for rare or admin-only shortnames so they cannot leak out of shops even if a shop owner adds them.

  • Integration guards

    Return true when a supporting plugin (currency, rank) is unloaded, preventing sales that the other plugin cannot credit or log.

Pitfalls

  • The original callback is skipped

    Returning non-null prevents the game from invoking onItemPurchased, so the hand-off, durability setup and any post-sale logic do not run. Do not fire it yourself unless you fully understand what it does.

  • Currency was already handled

    The hook fires after stock and currency bookkeeping earlier in DoTransaction. Blocking delivery here does not undo those changes; compensate manually if needed.

  • Broad filtering

    Returning true for everything silently disables every vending machine on the map. Keep conditions narrow and log configuration mistakes at load time.

Build a plugin with CanPurchaseItem

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Per-player purchase limits using the CanPurchaseItem hook, with a config file and a permission
Generate this plugin

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.

Build a plugin with CanPurchaseItem

Generate