# Example game — Rock, Paper, Scissors

> A complete, small game that uses every integration in this wiki. Play it on the dev server, download the project, and run it yourself in two minutes.

## Play it
**[Open the example game](https://platform-wiki-dev.karizmastudios.org/example/)** (development servers: platform-dev, platform-web-dev, pay-dev).

**[Download the project (.zip)](https://platform-wiki-dev.karizmastudios.org/downloads/baziness-example-rps.zip)**: plain Node.js and browser JavaScript, **no dependencies**, no build step.

## What it shows
A best-of-five rock-paper-scissors match against a bot: first to **3 points** wins, and draws don't count. Each match costs **1 ticket**, and winning pays **50 Gita coins**. Every platform feature is in it:

| Feature | Where in the game | Wiki page | Code |
| --- | --- | --- | --- |
| Sign in with Baziness (splash choice), account switch, logout | first launch; Settings → «اتصال حساب» | [Login](login.md) | `public/js/platform.js`, `server/services.js` (`platformToken`, `platformLogin`) |
| Currencies: tickets (local) and Gita coins (`get-coin`) | the top bar chips, the reward banner | [Gita coins](coins.md) | `public/js/platform.js` (`getCoin`), `public/js/app.js` (`renderTopbar`) |
| Winner gets 50 Gita coins (`add-coin`, idempotent), sign-in required to claim | the result popup, the "reward waiting" strip | [Gita coins](coins.md#players-who-havent-signed-in) | `server/services.js` (`claimMatchReward`), `public/js/app.js` (`claim`) |
| «انتخاب جایزه» reward page | the purple banner | [Reward page](rewards.md) | `public/js/platform.js` (`openRewardSelection`) |
| Tutorial: rules → coach mark → game-offer page → «خرید موفق» starter pack (5 tickets) | first launch | [Tutorial offer page](game-offer.md) | `public/js/app.js` (tutorial section), `server/services.js` (`claimStarterPack`) |
| Swipe-down sheet | every Baziness page | [Bottom sheet](bottom-sheet.md) | `public/js/sheet.js`, `public/styles.css` |
| Baziness Pay: «بسته‌ی بلیت», 10 tickets for 4,900 Toman (SKU `example_pack`) | the shop card | [Baziness Pay](pay-api.md) | `server/pay.js`, `server/services.js` (`startPurchase`, `verifyPurchase`), `public/js/purchase.js` |
| UTM / `?tag=` first-touch capture | any link, e.g. `…/example/?utm_source=wiki&utm_medium=docs` | [Links & UTM](links.md) | `public/js/api.js` (`FIRST_TOUCH`), `server/services.js` (`ensureUser`) |

## Run it locally
```bash
unzip baziness-example-rps.zip && cd baziness-example-rps
cp .env.example .env        # fill in BAZINESS_CLIENT_ID / BAZINESS_CLIENT_SECRET
npm start                   # → http://127.0.0.1:4380/
```

Requirements: **Node.js 20+**. Data lives in `data/db.json`, a tiny JSON store standing in for your database.

| `.env` | Development value |
| --- | --- |
| `BAZINESS_CLIENT_ID` / `BAZINESS_CLIENT_SECRET` | the example's dev client (ask the platform team, or use your own game's) |
| `BAZINESS_PLATFORM_PACKAGE_NAME` | `com.karizmastudio.example` |
| `BAZINESS_PAY_BASE_URL` | `https://pay-dev.karizmastudios.org` |
| `BAZINESS_PAY_PACKAGE_NAME` | `com.karizmastudio.example` |
| `PACK_SKU` / `PACK_PRICE` | `example_pack` / `4900` |

> [!TIP]
> **No credentials?** It still runs. With `BAZINESS_PLATFORM_ENABLED=false` the Baziness UI disappears and winnings are kept locally. With `DEV_FAKE_BAZINESS_PAY=true` and `BAZINESS_PAY_BASE_URL=http://localhost:4380/dev/baziness`, purchases go through a built-in fake gateway with a «پرداخت موفق» button.

## Testing tips
- **Signing in:** on this dev deployment, «ورود با حساب بازینس» accepts **any phone number with the code `111111`**. No SMS is sent.
- **Replay the tutorial:** use «بازنشانی این دستگاه» in the lobby footer. It clears local storage, so the next boot is a brand-new player.
- **A second player in the same browser:** add `?device=someone-else` to the URL.
- **Claiming as a guest:** win a match without signing in. The claim answers `PLATFORM_NOT_LINKED` and the login sheet opens. After you sign in, the claim retries by itself.
- **A purchase you finish later:** start a purchase, pay, close the game, and open it again. The lobby reconciles pending payments.
- **Campaign links:** open `…/example/?utm_source=wiki&utm_campaign=test`. The address is clean after load, and the tags are stored on the account when it is created.

## Project layout
```text
baziness-example-rps/
├── .env.example          every setting, documented
├── package.json          "npm start" — no dependencies
├── server/
│   ├── index.js          HTTP routes + static files (relative paths: works at / or /example/)
│   ├── env.js            configuration (.env loader)
│   ├── platform.js       platform client: auth, create-user, tokens, add-coin   ← secret stays here
│   ├── pay.js            Baziness Pay: buy URL, get_payment, verify_payment (form), consume
│   ├── services.js       accounts & linking, match rules, rewards, starter pack, purchases
│   ├── store.js          JSON-file store (swap for your database)
│   └── fake-pay.js       local fake gateway (never in production)
└── public/
    ├── index.html, styles.css
    ├── assets/           art (hands, bot, pack, prize, Gita coin, ticket, Baziness logo)
    └── js/
        ├── api.js        first module: UTM capture, device id, fetch wrapper
        ├── platform.js   BazinessPlatform: page URLs, messages, login, get-coin
        ├── sheet.js      the swipe-down sheet
        ├── purchase.js   Pay popup: tab opened in the click, verify on focus
        ├── ui.js         DOM helpers, Persian numbers, toasts, modals
        └── app.js        splash, lobby, match, tutorial
```

## Hosting it under a path
The client only uses **relative** URLs (`api/…`, `assets/…`), so the same files work at `/` or under a prefix. On the wiki's dev server, nginx strips `/example/` and proxies to the Node process:

```nginx
location = /example { return 301 /example/; }
location /example/ {
    proxy_pass http://127.0.0.1:4380/;          # trailing slash: /example/api/me → /api/me
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
```
