diff --git a/src/main/java/de/epiceric/shopchest/api/ShopChest.java b/src/main/java/de/epiceric/shopchest/api/ShopChest.java new file mode 100644 index 0000000..0d79bf0 --- /dev/null +++ b/src/main/java/de/epiceric/shopchest/api/ShopChest.java @@ -0,0 +1,29 @@ +package de.epiceric.shopchest.api; + +import org.bukkit.OfflinePlayer; + +/** + * The plugin's main entry point + * + * @since 1.13 + */ +public interface ShopChest { + + /** + * Gets an instance of the shop manager + * + * @return the shop manager + */ + ShopManager getShopManager(); + + /** + * Gets the amount of shops the given player is allowed to have + *
+ * If the player has no shop limit {@link Integer#MAX_VALUE} is returned.
+ *
+ * @param player the player
+ * @return the shop limit
+ */
+ int getShopLimit(OfflinePlayer player);
+
+}
\ No newline at end of file
diff --git a/src/main/java/de/epiceric/shopchest/api/ShopManager.java b/src/main/java/de/epiceric/shopchest/api/ShopManager.java
new file mode 100644
index 0000000..8e89ee5
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/ShopManager.java
@@ -0,0 +1,134 @@
+package de.epiceric.shopchest.api;
+
+import java.util.Collection;
+import java.util.Optional;
+
+import org.bukkit.Location;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.World;
+
+import de.epiceric.shopchest.api.event.ShopReloadEvent;
+import de.epiceric.shopchest.api.exceptions.ChestNotFoundException;
+import de.epiceric.shopchest.api.exceptions.NoSpaceAboveChestException;
+import de.epiceric.shopchest.api.shop.Shop;
+import de.epiceric.shopchest.api.shop.ShopProduct;
+
+/**
+ * Collection of methods to get, add or remove shops
+ *
+ * @since 1.13
+ */
+public interface ShopManager {
+
+ /**
+ * Gets all shops
+ *
+ * @return a collection of shops
+ */
+ Collection
+ * When {@code buyPrice} is zero, a player cannot buy from the shop.
+ * When {@code sellPrice} is zero, a player cannot sell to the shop.
+ * You cannot have {@code buyPrice} and {@code sellPrice} be zero.
+ *
+ * @param vendor the shop's vendor
+ * @param product the shop's product
+ * @param location the shop's chest location.
+ * Can be either chest if it's on a double chest
+ * @param buyPrice the price a player can buy the product for.
+ * @param sellPrice the price a player can sell the product for.
+ * @throws ChestNotFoundException when there is no chest at the given location
+ * @throws NoSpaceAboveChestException when there is no empty space above the chest
+ * @return the shop if created successfully or an empty optional if not
+ * @since 1.13
+ * @see ShopManager#addAdminShop(ShopProduct, Location, double, double)
+ */
+ Shop addShop(OfflinePlayer vendor, ShopProduct product, Location location, double buyPrice, double sellPrice)
+ throws ChestNotFoundException, NoSpaceAboveChestException;
+
+ /**
+ * Creates an admin shop and adds it to the database
+ *
+ * When {@code buyPrice} is zero, a player cannot buy from the shop.
+ * When {@code sellPrice} is zero, a player cannot sell to the shop.
+ * You cannot have {@code buyPrice} and {@code sellPrice} be zero.
+ *
+ * @param product the shop's product
+ * @param location the shop's chest location.
+ * Can be either chest if it's on a double chest
+ * @param buyPrice the price a player can buy the product for.
+ * @param sellPrice the price a player can sell the product for.
+ * @throws ChestNotFoundException when there is no chest at the given location
+ * @throws NoSpaceAboveChestException when there is no empty space above the chest
+ * @return the created admin shop
+ * @since 1.13
+ * @see ShopManager#addShop(OfflinePlayer, ShopProduct, Location, double, double)
+ */
+ Shop addAdminShop(ShopProduct product, Location location, double buyPrice, double sellPrice)
+ throws ChestNotFoundException, NoSpaceAboveChestException;
+
+ /**
+ * Removes a shop from the database
+ *
+ * @param shop the shop to remove
+ * @since 1.13
+ */
+ void removeShop(Shop shop);
+
+ /**
+ * Removes a shop from the database by its ID
+ *
+ * @param shopId the id of the shop to remove
+ * @since 1.13
+ */
+ void removeShop(int shopId);
+
+ /**
+ * Asynchronously reloads all shops from the database
+ *
+ * This does not trigger the {@link ShopReloadEvent}.
+ *
+ * @since 1.13
+ */
+ void reloadShops();
+
+}
\ No newline at end of file
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopBuySellEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopBuySellEvent.java
new file mode 100644
index 0000000..9fbd8e4
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopBuySellEvent.java
@@ -0,0 +1,59 @@
+package de.epiceric.shopchest.api.event;
+
+import de.epiceric.shopchest.api.shop.Shop;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+
+/**
+ * Called when a player buys or sells something from or to a shop
+ */
+public class ShopBuySellEvent extends ShopEvent implements Cancellable {
+ private Type type;
+ private int amount;
+ private double price;
+ private boolean cancelled;
+
+ public ShopBuySellEvent(Player player, Shop shop, Type type, int amount, double price) {
+ super(player, shop);
+ this.type = type;
+ this.amount = amount;
+ this.price = price;
+ }
+
+ /**
+ * @return Whether the player buys or sells something
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * @return The amount which might be modified because of automatic item amount calculation
+ */
+ public int getAmount() {
+ return amount;
+ }
+
+ /**
+ * @return The price which might be modified because of automatic item amount calculation
+ */
+ public double getPrice() {
+ return price;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+
+ public enum Type {
+ BUY,
+ SELL;
+ }
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopCreateEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopCreateEvent.java
new file mode 100644
index 0000000..4e23a88
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopCreateEvent.java
@@ -0,0 +1,35 @@
+package de.epiceric.shopchest.api.event;
+
+import de.epiceric.shopchest.api.shop.Shop;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+
+/**
+ * Called when a player creates a shop (clicks on a chest)
+ */
+public class ShopCreateEvent extends ShopEvent implements Cancellable {
+ private double creationPrice;
+ private boolean cancelled;
+
+ public ShopCreateEvent(Player player, Shop shop, double creationPrice) {
+ super(player, shop);
+ this.creationPrice = creationPrice;
+ }
+ /**
+ * @return The price the player has to pay in order to create the shop (only if the event is not cancelled)
+ */
+ public double getCreationPrice() {
+ return creationPrice;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopEvent.java
new file mode 100644
index 0000000..0022ea3
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopEvent.java
@@ -0,0 +1,42 @@
+package de.epiceric.shopchest.api.event;
+
+import de.epiceric.shopchest.api.shop.Shop;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+public abstract class ShopEvent extends Event {
+ private static final HandlerList handlers = new HandlerList();
+
+ private Shop shop;
+ private Player player;
+
+ public ShopEvent(Player player, Shop shop) {
+ this.player = player;
+ this.shop = shop;
+ }
+
+ /**
+ * @return Shop which is involved in this event
+ */
+ public Shop getShop() {
+ return shop;
+ }
+
+ /**
+ * @return Player who is involved in this event
+ */
+ public Player getPlayer() {
+ return player;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopExtendEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopExtendEvent.java
new file mode 100644
index 0000000..59535e6
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopExtendEvent.java
@@ -0,0 +1,37 @@
+package de.epiceric.shopchest.api.event;
+
+import de.epiceric.shopchest.api.shop.Shop;
+
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+
+/**
+ * Called when a player extends a shop (making a chest a double chest)
+ */
+public class ShopExtendEvent extends ShopEvent implements Cancellable {
+ private boolean cancelled;
+ private Location newChestLocation;
+
+ public ShopExtendEvent(Player player, Shop shop, Location newChest) {
+ super(player, shop);
+ this.newChestLocation = newChest;
+ }
+
+ /**
+ * @return Location of the placed chest
+ */
+ public Location getNewChestLocation() {
+ return newChestLocation;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopInfoEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopInfoEvent.java
new file mode 100644
index 0000000..483b95e
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopInfoEvent.java
@@ -0,0 +1,26 @@
+package de.epiceric.shopchest.api.event;
+
+import de.epiceric.shopchest.api.shop.Shop;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+
+/**
+ * Called when a player retrieves information about a shop (clicks on a chest)
+ */
+public class ShopInfoEvent extends ShopEvent implements Cancellable {
+ private boolean cancelled;
+
+ public ShopInfoEvent(Player player, Shop shop) {
+ super(player, shop);
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopInitializedEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopInitializedEvent.java
new file mode 100644
index 0000000..fe07b86
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopInitializedEvent.java
@@ -0,0 +1,27 @@
+package de.epiceric.shopchest.api.event;
+
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+public class ShopInitializedEvent extends Event {
+ private static final HandlerList handlers = new HandlerList();
+
+ private int amount;
+
+ public ShopInitializedEvent(int amount) {
+ this.amount = amount;
+ }
+
+ public int getAmount() {
+ return amount;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopOpenEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopOpenEvent.java
new file mode 100644
index 0000000..b23c230
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopOpenEvent.java
@@ -0,0 +1,26 @@
+package de.epiceric.shopchest.api.event;
+
+import de.epiceric.shopchest.api.shop.Shop;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+
+/**
+ * Called when a player opens a shop (clicks on a chest)
+ */
+public class ShopOpenEvent extends ShopEvent implements Cancellable {
+ private boolean cancelled;
+
+ public ShopOpenEvent(Player player, Shop shop) {
+ super(player, shop);
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopPreCreateEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopPreCreateEvent.java
new file mode 100644
index 0000000..a2e5b02
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopPreCreateEvent.java
@@ -0,0 +1,84 @@
+package de.epiceric.shopchest.api.event;
+
+import de.epiceric.shopchest.api.shop.ShopProduct;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+/**
+ * Called when a player wants to create a shop (enters the command)
+ */
+public class ShopPreCreateEvent extends Event implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+
+ private Player player;
+ private ShopProduct product;
+ private double buyPrice;
+ private double sellPrice;
+ private boolean admin;
+ private boolean cancelled;
+
+ public ShopPreCreateEvent(Player player, ShopProduct product, double buyPrice, double sellPrice, boolean admin) {
+ this.player = player;
+ this.product = product;
+ this.buyPrice = buyPrice;
+ this.sellPrice = sellPrice;
+ this.admin = admin;
+ }
+
+ /**
+ * @return the player
+ */
+ public Player getPlayer() {
+ return player;
+ }
+
+ /**
+ * @return the product
+ */
+ public ShopProduct getProduct() {
+ return product;
+ }
+
+ /**
+ * @return the buyPrice
+ */
+ public double getBuyPrice() {
+ return buyPrice;
+ }
+
+ /**
+ * @return the sellPrice
+ */
+ public double getSellPrice() {
+ return sellPrice;
+ }
+
+ /**
+ * @return the admin
+ */
+ public boolean isAdminShop() {
+ return admin;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopPreInfoEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopPreInfoEvent.java
new file mode 100644
index 0000000..e2305dc
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopPreInfoEvent.java
@@ -0,0 +1,46 @@
+package de.epiceric.shopchest.api.event;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+/**
+ * Called when a player wants to retrieve information about a shop (enters the command)
+ */
+public class ShopPreInfoEvent extends Event implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+
+ private Player player;
+ private boolean cancelled;
+
+ public ShopPreInfoEvent(Player player) {
+ this.player = player;
+ }
+
+ /**
+ * @return Player who is involved in this event
+ */
+ public Player getPlayer() {
+ return player;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopPreOpenEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopPreOpenEvent.java
new file mode 100644
index 0000000..885f9da
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopPreOpenEvent.java
@@ -0,0 +1,46 @@
+package de.epiceric.shopchest.api.event;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+/**
+ * Called when a player wants to open a shop (enters the command)
+ */
+public class ShopPreOpenEvent extends Event implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+
+ private Player player;
+ private boolean cancelled;
+
+ public ShopPreOpenEvent(Player player) {
+ this.player = player;
+ }
+
+ /**
+ * @return Player who is involved in this event
+ */
+ public Player getPlayer() {
+ return player;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopPreRemoveEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopPreRemoveEvent.java
new file mode 100644
index 0000000..95d8c0f
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopPreRemoveEvent.java
@@ -0,0 +1,46 @@
+package de.epiceric.shopchest.api.event;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+/**
+ * Called when a player wants to remove a shop (enters the command)
+ */
+public class ShopPreRemoveEvent extends Event implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+
+ private Player player;
+ private boolean cancelled;
+
+ public ShopPreRemoveEvent(Player player) {
+ this.player = player;
+ }
+
+ /**
+ * @return Player who is involved in this event
+ */
+ public Player getPlayer() {
+ return player;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopReloadEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopReloadEvent.java
new file mode 100644
index 0000000..0e6c521
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopReloadEvent.java
@@ -0,0 +1,46 @@
+package de.epiceric.shopchest.api.event;
+
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+/**
+ * Called when a player reloads the shops
+ */
+public class ShopReloadEvent extends Event implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+
+ private CommandSender sender;
+ private boolean cancelled;
+
+ public ShopReloadEvent(CommandSender sender) {
+ this.sender = sender;
+ }
+
+ /**
+ * @return Sender who triggered the reload
+ */
+ public CommandSender getSender() {
+ return sender;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ cancelled = cancel;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+}
diff --git a/src/main/java/de/epiceric/shopchest/api/event/ShopRemoveAllEvent.java b/src/main/java/de/epiceric/shopchest/api/event/ShopRemoveAllEvent.java
new file mode 100644
index 0000000..85c5d3a
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/event/ShopRemoveAllEvent.java
@@ -0,0 +1,56 @@
+package de.epiceric.shopchest.api.event;
+
+import de.epiceric.shopchest.shop.Shop;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+import java.util.List;
+
+public class ShopRemoveAllEvent extends Event implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+
+ private CommandSender sender;
+ private OfflinePlayer vendor;
+ private List
+ * If the shop is on a double chest, it returns one of the chest's location.
+ *
+ * @return the location
+ * @since 1.13
+ */
+ Location getLocation();
+
+ /**
+ * Gets the world this shop is located in
+ *
+ * @return the world
+ * @since 1.13
+ */
+ default World getWorld() {
+ return getLocation().getWorld();
+ }
+
+ /**
+ * Gets whether this shop is on a double chest
+ *
+ * @return whether the shop is on a double chest
+ * @since 1.13
+ */
+ boolean isDoubleChest();
+
+ /**
+ * Gets the inventory of this shop's chest
+ *
+ * @return the inventory
+ * @since 1.13
+ */
+ Inventory getInventory();
+
+ /**
+ * Gets whether this shop is an admin shop
+ *
+ * @return whether this shop is an admin shop
+ * @since 1.13
+ */
+ boolean isAdminShop();
+
+ /**
+ * Sets whether this shop is an admin shop
+ *
+ * @param adminShop whether this shop should be an admin shop
+ * @since 1.13
+ */
+ void setAdminShop(boolean adminShop);
+
+ /**
+ * Gets the price for which a player can buy the product from this shop
+ *
+ * @return the buy price
+ * @since 1.13
+ */
+ double getBuyPrice();
+
+ /**
+ * Sets the price for which a player can sell the product to this shop
+ *
+ * If set to zero, a player cannot buy from this shop.
+ *
+ * @param buyPrice the buy price
+ * @throws IllegalStateException when a player can neither buy nor sell from this shop
+ * @since 1.13
+ */
+ void setBuyPrice(double buyPrice);
+
+ /**
+ * Gets whether a player can buy from this shop
+ *
+ * @return whether buying is enabled
+ * @since 1.13
+ */
+ default boolean canPlayerBuy() {
+ return getBuyPrice() > 0;
+ }
+
+ /**
+ * Gets the price for which a player can sell the product to this shop
+ *
+ * @return the sell price
+ * @since 1.13
+ */
+ double getSellPrice();
+
+ /**
+ * Sets the price for which a player can sell the product to this shop
+ *
+ * If set to zero, a player cannot sell to this shop.
+ *
+ * @param sellPrice the sell price
+ * @throws IllegalStateException when a player can neither buy nor sell from this shop
+ * @since 1.13
+ */
+ void setSellPrice(double sellPrice);
+
+ /**
+ * Gets whether a player can sell to this shop
+ *
+ * @return whether selling is enabled
+ * @since 1.13
+ */
+ default boolean canPlayerSell() {
+ return getSellPrice() > 0;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/de/epiceric/shopchest/api/shop/ShopProduct.java b/src/main/java/de/epiceric/shopchest/api/shop/ShopProduct.java
new file mode 100644
index 0000000..73b5793
--- /dev/null
+++ b/src/main/java/de/epiceric/shopchest/api/shop/ShopProduct.java
@@ -0,0 +1,54 @@
+package de.epiceric.shopchest.api.shop;
+
+import org.bukkit.inventory.ItemStack;
+
+/**
+ * Represents the item that can be bought or sold in one transaction
+ *
+ * @since 1.13
+ */
+public interface ShopProduct {
+
+ /**
+ * Gets the {@link ItemStack} with an amount of one
+ *
+ * @return the item
+ * @since 1.13
+ */
+ ItemStack getItemStack();
+
+ /**
+ * Sets the {@link ItemStack}
+ *
+ * The passed item stack will be cloned and its amount set to one.
+ *
+ * @param itemStack the item
+ * @since 1.13
+ */
+ void setItemStack(ItemStack itemStack);
+
+ /**
+ * Gets the amount of items bought or sold in one transaction
+ *
+ * @return the amount
+ * @since 1.13
+ */
+ int getAmount();
+
+ /**
+ * Sets the amount of items bought or sold in one transaction
+ *
+ * @param amount the amount
+ * @since 1.13
+ */
+ void setAmount(int amount);
+
+ /**
+ * Gets the localized name of this product's item in the configured langauge file
+ *
+ * @return the localized name
+ * @since 1.13
+ */
+ String getLocalizedName();
+
+}
\ No newline at end of file