# Game auction houses and player markets

> Live listings, bids and countdowns in every client, a last-seconds extension against sniping, buy now and sale prices, with your server deciding every bid and your game keeping the coins and items.

Source: https://hibira.io/use-cases/game-auction-house

An auction house, trading post or player market keeps a game's economy moving, and it only feels fair when everyone sees the same thing at the same time: the new price the moment someone bids, the same countdown on every screen, and a sale that cannot be stolen in the last second. The open-source [@hibira-io/auction](https://www.npmjs.com/package/@hibira-io/auction) package gives you that on Hibira, with your own server deciding every bid.

## What your players get

- Listings that appear, change price and disappear in every open client, without refreshing.
- Countdowns that agree with the server's clock, and a bid in the last seconds that adds time instead of sniping.
- "You were outbid" the moment it happens, buy now, and the recent sale prices of each item.

![The auction house example: a player bids on Hunter's bow, a second window outbids them a moment later, and the first player sees the new price and an outbid notice.](https://hibira.io/portal/articles/demo-game-auction-house-en.webp)

## Built with

- **One public or private channel per house** (`auction-main`), carrying one event, `auction-update`, with the whole listing and a version number.
- **The HTTP API** (`trigger`) from your server, so each change is published once however many players watch it.
- **[@hibira-io/auction](https://github.com/hibira-io/auction-js)**: the server part checks bids, extends late auctions and settles them; the client part keeps the newest copy of each listing and catches up after a reconnect.

```text
sequence: Bids go to your server; Hibira tells every player
Player A | Your server | *Hibira | Player B
1 > 2: POST /auction/:id/bid | 120 gold
note: house.bid() checks the bid and holds the gold
2 > 3: auction-update | price 120, A leads
3 > 1: auction-update
3 > 4: auction-update | "Bid 126"
```

## Step 1: create the house on your server

Your game keeps the items and the coins. The house asks you to hold a bidder's coins, to give them back when someone bids higher, and to move the item and the coins when an auction closes:

```js
import Pusher from 'pusher';
import { createAuctionHouse } from '@hibira-io/auction/server';

const hibira = new Pusher({ appId, key, secret, host: 'ws.hibira.io', useTLS: true });

const house = createAuctionHouse({
  pusher: hibira,
  name: 'main',
  extendWithin: 30000, // a bid in the last 30 s adds time
  reserve: async ({ userId, amount }) => {
    if (!(await wallet.hold(userId, amount))) throw new Error('not enough gold');
  },
  release: async ({ userId, amount }) => wallet.unhold(userId, amount),
  settle: async ({ listing }) => game.deliver(listing), // idempotent by listing.id
});
house.start(); // closes and settles ended auctions
```

## Step 2: four endpoints

Take the player from your own session, never from the request body:

```js
app.get('/auction', async (req, res) => res.json(await house.snapshot()));

app.post('/auction/:id/bid', requireLogin, async (req, res) => {
  try {
    res.json(await house.bid({ listingId: req.params.id, bidderId: req.user.id, bidder: { name: req.user.name }, amount: req.body.amount }));
  } catch (err) {
    res.status(err.status ?? 500).json({ error: err.code });
  }
});
// The same for house.buyout() and house.list()
```

## Step 3: show the house in the game

```js
import { AuctionHouse } from '@hibira-io/auction';

const house = AuctionHouse.open(pusher, 'main', {
  load: () => fetch('/auction').then((r) => r.json()),
});

house.on('change', ({ listings }) => render(listings));
setInterval(() => listings.forEach((l) => showTimeLeft(l, house.timeLeft(l))), 250);

bidButton.onclick = () => fetch(`/auction/${l.id}/bid`, { method: 'POST', body: JSON.stringify({ amount: l.minBid }) });
```

`house.prices(type)` returns the recent sale prices of an item type for a price chart. After a reconnect the house loads the snapshot again, so nothing is missed.

## Try it

The [auction house example](https://github.com/hibira-io/examples/tree/main/nextjs-auction) is a complete Next.js game market: NPC traders, a bag, buy now, outbid notices and sale prices, in seven languages. Open it in two windows and bid in one.

## Tips

- Every change is one message, however many players watch. Split a large house by category or region (`name: 'weapons'`) so each client loads only what it shows.
- With several game servers, give the house a `store` on your database; saves are compare-and-set, so two servers cannot both accept the same bid.
- It moves your game's own coins and items, not real money.
- Clients on Unity, Godot or any Pusher SDK can follow the same `auction-main` channel and call the same endpoints. For rooms and matchmaking, see [multiplayer game rooms and lobbies](https://hibira.io/use-cases/game-lobbies).
