# Protokół pluginu

Ta strona jest dla osób, które chcą napisać własną integrację zamiast oficjalnego pluginu (np. dla innego silnika). Zwykły sprzedawca jej nie potrzebuje.

!!!
StillShop nie ma publicznego REST API dla sprzedawców ani widżetów do osadzania na innych stronach — tylko poniższe API serwerów gry.
!!!

## Endpointy

Wszystkie: `POST`, JSON, bazowy adres `https://stillshop.pl`.

| Endpoint | Do czego |
| --- | --- |
| `/api/v1/servers/handshake` | Przedstawienie się serwera przy starcie. |
| `/api/v1/servers/heartbeat` | Liczba graczy i status (co `heartbeatSeconds`). |
| `/api/v1/servers/poll` | Pobranie komend do wykonania (long-poll do 25 s). |
| `/api/v1/servers/confirm` | Potwierdzenie wyników wykonania. |

### handshake

```json
{ "pluginVersion": "1.0.0", "minecraftVersion": "1.21.4", "serverType": "PAPER", "onlinePlayers": 12 }
```

`serverType`: `PAPER`, `SPIGOT`, `FOLIA`, `VELOCITY` albo `BUNGEE`. Odpowiedź:

```json
{
  "protocol": "SS1",
  "serverTime": "2026-11-05T12:00:00.000Z",
  "server": { "id": "…", "name": "Survival" },
  "store": { "id": "…", "slug": "stillcraft", "name": "StillCraft" },
  "settings": { "longPollSeconds": 25, "leaseSeconds": 120, "maxBatch": 100, "heartbeatSeconds": 60 }
}
```

### heartbeat

```json
{ "onlinePlayers": 12, "tps": 19.9, "pluginVersion": "1.0.0", "deferredCount": 3 }
```

Odpowiedź: `{ "serverTime": "…", "queued": 0 }`.

### poll

```json
{ "limit": 50, "waitSeconds": 25 }
```

Odpowiedź:

```json
{
  "serverTime": "…",
  "claimToken": "…",
  "leaseSeconds": 120,
  "commands": [
    {
      "id": "…",
      "command": "lp user Notch parent addtemp vip 30d",
      "trigger": "ON_PURCHASE_IMMEDIATE",
      "requiresOnline": false,
      "playerUuid": null,
      "playerNick": "Notch",
      "orderId": "…",
      "attempt": 1
    }
  ]
}
```

Pobrane komendy są „wypożyczone” na `leaseSeconds` (120 s). Jeśli nie potwierdzisz ich w tym czasie, wrócą do kolejki.

### confirm

```json
{
  "claimToken": "…",
  "results": [
    { "id": "…", "status": "EXECUTED", "executedAt": "2026-11-05T12:00:01.000Z" },
    { "id": "…", "status": "FAILED", "error": "Unknown command", "retryable": false },
    { "id": "…", "status": "DEFERRED" }
  ]
}
```

- `EXECUTED` — wykonana.
- `FAILED` — błąd; `retryable: true` (domyślnie) oznacza ponowienie z rosnącym odstępem (15 s × 2ⁿ, do 1 h, maks. 5 prób).
- `DEFERRED` — czeka na gracza online; integracja przechowuje ją sama i potwierdza `EXECUTED` po wykonaniu (lease przedłuża się o 30 dni).

Odpowiedź: `{ "serverTime": "…", "accepted": ["…"], "rejected": ["…"] }`. Potwierdzenia z wygasłego lease są odrzucane.

## Podpisy (SS1)

Każde zapytanie ma nagłówki:

| Nagłówek | Wartość |
| --- | --- |
| `X-StillShop-Key` | `api-key` (`ssk_…`) |
| `X-StillShop-Timestamp` | Czas w milisekundach (13 cyfr) |
| `X-StillShop-Nonce` | Losowy ciąg 16–64 znaków `[A-Za-z0-9_-]`, jednorazowy |
| `X-StillShop-Signature` | HMAC-SHA256 (hex, małe litery) |

Podpisywany tekst (linie rozdzielone `\n`):

```
SS1-REQ
POST
/api/v1/servers/poll
1730808000000
<nonce>
<sha256 hex surowego body>
```

Kluczem HMAC są bajty UTF-8 `server-secret` (`sss_…`).

Odpowiedzi są podpisane tak samo w nagłówkach `X-StillShop-Timestamp` i `X-StillShop-Signature`, nad tekstem:

```
SS1-RES
<kod HTTP>
<nonce z zapytania>
<timestamp odpowiedzi>
<sha256 hex body odpowiedzi>
```

!!!danger Weryfikuj odpowiedzi
Odpowiedzi zawierają komendy konsoli. Odrzuć każdą odpowiedź bez poprawnego podpisu.
!!!

## Ograniczenia

- Różnica zegara: maks. 5 minut.
- Nonce nie może się powtórzyć przez 15 minut.
- Body: maks. 64 KiB.
- 240 zapytań na minutę na klucz (`429 rate_limited`).
- Każdy błąd uwierzytelnienia zwraca `401 {"error":"unauthorized"}` bez szczegółów.
