> For the complete documentation index, see [llms.txt](/llms.txt)

# GS2-Showcase

Product sales feature



This feature is used to sell products in the game.

The difference from GS2-Exchange is the existence of a showcase.
DisplayItems can be placed on a showcase, and you can configure the cost required to purchase a DisplayItem and the reward obtained when purchasing it.

There are two types of showcases: the "Standard Showcase," on which fixed DisplayItems are placed, and the "Random Showcase," on which the displayed contents are randomly drawn at regular intervals.

```mermaid
graph TD
  Master["Master data"] --> Showcase["Standard showcase<br/>(ShowcaseModel)"]
  Master --> RandomShowcase["Random showcase<br/>(RandomShowcaseModel)"]
  Showcase --> DisplayItem["DisplayItem<br/>(SalesItem / SalesItemGroup)"]
  RandomShowcase --> RandomDisplayItem["RandomDisplayItem<br/>(stock / weight)"]
  Player["Player"] -- Buy --> DisplayItem
  Player -- RandomShowcaseBuy --> RandomDisplayItem
  DisplayItem -- "consumeActions / acquireActions" --> Transaction["GS2-Distributor<br/>execute transaction"]
  RandomDisplayItem -- "consumeActions / acquireActions" --> Transaction
```

## Standard Showcase

On a standard showcase, all of the specified DisplayItems are placed.
There are two types of DisplayItem: "SalesItem" and "SalesItemGroup."

### SalesItem

A SalesItem can be configured with the cost required to purchase it and the reward obtained when purchased.

A SalesItem expresses its behavior using the following three kinds of actions.

| Item | Description |
| --- | --- |
| `verifyActions` | Verify actions for purchase conditions. Verifies the state of the specified microservice, and rejects the purchase if the conditions are not met. |
| `consumeActions` | Actions consumed at the time of purchase. Currency in GS2-Money2 or items in GS2-Inventory, etc., are consumed as the cost. |
| `acquireActions` | Actions to acquire at the time of purchase. Distributes rewards to any microservice such as GS2-Inventory / GS2-Experience / GS2-Lottery. |

### SalesItemGroup

A SalesItemGroup is a mechanism in which the SalesItem being sold changes depending on the number of purchases.

A SalesItemGroup can contain multiple SalesItems, and for all but the last item in the list, a GS2-Limit counter increment must be set as the cost.
When a SalesItemGroup is placed on a showcase, the system determines whether the internal SalesItems are purchasable, and the first SalesItem determined to be purchasable is displayed.

By using this feature, you can sell an item at half price for first purchases only, realize items whose price rises with each purchase, or add a bonus on the 10th purchase, and so on.

```mermaid
graph LR
  Buy["Purchase request"] --> Check1{"SalesItem 1<br/>(first-time only)"}
  Check1 -- Purchasable --> Sell1["Sell SalesItem 1"]
  Check1 -- Already purchased --> Check2{"SalesItem 2<br/>(2nd to 9th time)"}
  Check2 -- Purchasable --> Sell2["Sell SalesItem 2"]
  Check2 -- Limit reached --> Check3{"SalesItem 3<br/>(10th-time bonus)"}
  Check3 -- Purchasable --> Sell3["Sell SalesItem 3"]
```

## Random Showcase

On a random showcase, a specified number of DisplayItems are randomly drawn from those specified in master data and placed.

A random showcase is controlled by the following parameters.

| Item | Description |
| --- | --- |
| `maximumNumberOfChoice` | The maximum number of DisplayItems placed in a single drawing |
| `displayItems` | The list of RandomDisplayItems eligible for drawing. Each item has a `weight` (drawing weight) and `stock` (inventory). |
| `baseTimestamp` / `resetIntervalHours` | The interval at which the displayed contents are redrawn. The display is refreshed at a fixed interval starting from the specified time. |
| `salesPeriodEventId` | A GS2-Schedule event ID. Specify this when you want to restrict the sales period. |

The drawing result is stored per player as `RandomShowcaseStatus`, and stock is consumed at the time of purchase. When the redraw interval is reached, the displayed contents are updated on the next access.

## Adjustment via Buffs

By integrating with GS2-Buff, you can dynamically adjust `acquireActions`, `verifyActions`, and `consumeActions` of DisplayItem and RandomDisplayItemModel. For random display items, the `stock` can also be overwritten. This allows flexible modification of rewards, required costs, and inventory quantities to align with events or campaigns.

## Script Triggers

By setting `buyScript` on the namespace, you can invoke a custom script at the timing of an item purchase.

The main configurable event triggers and script setting names are as follows:

- `buyScript`: When an item is purchased

By leveraging these, you can insert custom validation, audit log output, KPI aggregation, etc., into the purchase process.

## Transaction Actions

GS2-Showcase provides the following transaction actions:

- Consume actions: increment purchase count
- Acquire actions: decrement purchase count, force re-draw of a random showcase

By using "force re-draw of a random showcase" as an acquire action, you can safely execute processes such as forcefully updating the shop's displayed items as a reward when a specific item is obtained or a quest is cleared, all within a transaction. This enables providing a product lineup that is timely and aligned with the player's progress. Additionally, by using "decrement purchase count," operations such as individually restoring product purchase limits (for example, re-releasing limited items) are also facilitated.

## Master Data Operation

Registering master data allows you to configure data and behaviors available to the microservice.

Master data types include the following:

- `ShowcaseModel`: Definition of a standard showcase. You can place `SalesItem` or `SalesItemGroup` as `DisplayItem`.
- `RandomShowcaseModel`: Definition of a random showcase. `maximumNumberOfChoice` items are drawn from a list of `RandomDisplayItemModel` and displayed.
- `SalesItem` / `SalesItemMaster`: Definition of a single product for sale.
- `SalesItemGroup` / `SalesItemGroupMaster`: Definition of a product group whose displayed contents change depending on the purchase status.

Master data can be registered via the Management Console, by reflecting data from GitHub, or by setting up workflows to register via CI using GS2-Deploy.

The following is a JSON example of master data including a random showcase.

```json
{
  "version": "2019-09-13",
  "showcases": [
    {
      "name": "showcase-0001",
      "metadata": "Regular shop",
      "salesPeriodEventId": null,
      "displayItems": [
        {
          "displayItemId": "display-item-0001",
          "type": "salesItem",
          "salesItemName": "item-0001"
        }
      ]
    }
  ],
  "randomShowcases": [
    {
      "name": "random-showcase-0001",
      "metadata": "Daily shop",
      "maximumNumberOfChoice": 4,
      "baseTimestamp": 1700000000000,
      "resetIntervalHours": 24,
      "displayItems": [
        {
          "name": "display-item-0001",
          "weight": 10,
          "stock": 3,
          "consumeActions": [],
          "acquireActions": []
        }
      ]
    }
  ]
}
```

## Example Implementation

### Standard Showcase

#### Get a showcase



**Unity**
```csharp

    var item = await gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Showcase(
        showcaseName: "showcase-0001"
    ).ModelAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Showcase(
        "showcase-0001" // showcaseName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto Result = Future->GetTask().Result();
```


#### Get a list of showcases

An API for retrieving the list of showcases is not provided in the game engine SDK.
If you need to retrieve it, please use the Management Console / GS2 CLI / general SDKs for each language (C# / Go / Python / TypeScript / PHP / Java).

#### Buy an item



**Unity**
```csharp

    var result = await gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Showcase(
        showcaseName: "showcase-0001"
    ).DisplayItem(
        displayItemId: "display-item-0001"
    ).BuyAsync(
        quantity: 1,
        config: null
    );

    await result.WaitAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Showcase(
        "showcase-0001" // showcaseName
    )->DisplayItem(
        "display-item-0001" // displayItemId
    );
    const auto Future = Domain->Buy(
        1, // quantity
        nullptr // config
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    const auto Transaction = Future->GetTask().Result();
    const auto Future2 = Transaction->Wait();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
```


`Buy` is executed as a transaction processed through GS2-Distributor. To reflect the transaction result, call `WaitAsync` / `Wait` on the returned `EzTransactionDomain` and wait for the transaction to complete.

### Random Showcase

#### Get a showcase



**Unity**
```csharp

    var items = await gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    ).RandomDisplayItemsAsync(
    ).ToListAsync();
```
**Unreal Engine**
```cpp

    const auto It = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->RandomShowcase(
        "showcase-0001" // showcaseName
    )->RandomDisplayItems(
    );
    TArray<Gs2::UE5::Showcase::Model::FEzRandomDisplayItemPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
```


#### Buy an item



**Unity**
```csharp

    var result = await gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    ).RandomDisplayItem(
        displayItemName: "display-item-0001"
    ).RandomShowcaseBuyAsync(
        quantity: 1,
        config: null
    );

    await result.WaitAsync();
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->RandomShowcase(
        "showcase-0001" // showcaseName
    )->RandomDisplayItem(
        "display-item-0001" // displayItemName
    )->RandomShowcaseBuy(
        1, // quantity
        nullptr // config
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }
    const auto Transaction = Future->GetTask().Result();
    const auto Future2 = Transaction->Wait();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
```


#### Get the state of a random showcase

A random showcase holds, per player, the drawing result and purchase count as `RandomShowcaseStatus`.
Retrieve this when you want to check the next redraw time or the number of items already purchased.



**Unity**
```csharp

    var item = await gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcaseStatus(
        showcaseName: "showcase-0001"
    ).ModelAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->RandomShowcaseStatus(
        "showcase-0001" // showcaseName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto Result = Future->GetTask().Result();
```


## Detailed Reference
[GS2-Showcase API Reference](../../api_reference/showcase)



