# بازی نمونه — سنگ، کاغذ، قیچی

> یک بازی کامل و کوچک که همه‌ی یکپارچه‌سازی‌های این ویکی را دارد. روی سرور توسعه بازی‌اش کنید، پروژه را دانلود کنید و در دو دقیقه خودتان اجرایش کنید.

## بازی کنید
**[باز کردن بازی نمونه](https://platform-wiki-dev.karizmastudios.org/example/)** (سرورهای توسعه: platform-dev، platform-web-dev، pay-dev).

**[دانلود پروژه (.zip)](https://platform-wiki-dev.karizmastudios.org/downloads/baziness-example-rps.zip)**: Node.js و جاوااسکریپت مرورگر ساده، **بدون هیچ وابستگی** و بدون مرحله‌ی بیلد.

## چه چیزهایی را نشان می‌دهد
یک مسابقه‌ی سنگ، کاغذ، قیچی «بهترین از ۵ دست» در برابر ربات: اولین کسی که **۳ امتیاز** بگیرد برنده است و دست مساوی امتیاز ندارد. هر مسابقه **۱ بلیت** هزینه دارد و برنده **۵۰ گیتا کوین** می‌گیرد. همه‌ی قابلیت‌های پلتفرم در آن هست:

| قابلیت | کجای بازی | صفحه‌ی ویکی | کد |
| --- | --- | --- | --- |
| ورود با بازینس (انتخاب اسپلش)، جابه‌جایی حساب، خروج | اجرای اول؛ تنظیمات ← «اتصال حساب» | [ورود](login.md) | `public/js/platform.js`، `server/services.js` (`platformToken`، `platformLogin`) |
| ارزها: بلیت (محلی) و گیتا کوین (`get-coin`) | نشان‌های نوار بالا، بنر جایزه | [گیتا کوین](coins.md) | `public/js/platform.js` (`getCoin`)، `public/js/app.js` (`renderTopbar`) |
| ۵۰ گیتا کوین برای برنده (`add-coin` با کلید یکتا)، برای دریافت ورود لازم است | پنجره‌ی نتیجه، نوار «جایزه منتظر توست» | [گیتا کوین](coins.md#players-who-havent-signed-in) | `server/services.js` (`claimMatchReward`)، `public/js/app.js` (`claim`) |
| صفحه‌ی «انتخاب جایزه» | بنر بنفش | [صفحه‌ی جایزه](rewards.md) | `public/js/platform.js` (`openRewardSelection`) |
| آموزش: قانون‌ها ← راهنما روی «انتخاب جایزه» ← صفحه‌ی game-offer ← «خرید موفق» بسته‌ی شروع (۵ بلیت) | اجرای اول | [صفحه‌ی پیشنهاد آموزش](game-offer.md) | `public/js/app.js` (بخش آموزش)، `server/services.js` (`claimStarterPack`) |
| شیت کشویی | همه‌ی صفحه‌های بازینس | [شیت پایین](bottom-sheet.md) | `public/js/sheet.js`، `public/styles.css` |
| پرداخت بازینس: «بسته‌ی بلیت»، ۱۰ بلیت به ۴٬۹۰۰ تومان (SKU `example_pack`) | کارت فروشگاه | [پرداخت بازینس](pay-api.md) | `server/pay.js`، `server/services.js` (`startPurchase`، `verifyPurchase`)، `public/js/purchase.js` |
| گرفتن UTM و `?tag=` به‌عنوان اولین تماس | هر لینکی، مثلاً `…/example/?utm_source=wiki&utm_medium=docs` | [لینک‌ها و UTM](links.md) | `public/js/api.js` (`FIRST_TOUCH`)، `server/services.js` (`ensureUser`) |

## اجرای محلی
```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/
```

پیش‌نیاز: **Node.js 20 یا بالاتر**. داده‌ها در `data/db.json` نگه داشته می‌شوند، یک ذخیره‌ساز JSON کوچک به جای دیتابیس شما.

| `.env` | مقدار توسعه |
| --- | --- |
| `BAZINESS_CLIENT_ID` / `BAZINESS_CLIENT_SECRET` | کلاینت توسعه‌ی نمونه (از تیم پلتفرم بگیرید، یا کلاینت بازی خودتان) |
| `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]
> **کلید ندارید؟** باز هم اجرا می‌شود. با `BAZINESS_PLATFORM_ENABLED=false` رابط بازینس پنهان می‌شود و جایزه‌ها محلی نگه داشته می‌شوند. با `DEV_FAKE_BAZINESS_PAY=true` و `BAZINESS_PAY_BASE_URL=http://localhost:4380/dev/baziness`، خریدها از یک درگاه آزمایشی داخلی با دکمه‌ی «پرداخت موفق» رد می‌شوند.

## نکته‌های تست
- **ورود:** روی این نسخه‌ی توسعه، «ورود با حساب بازینس» **هر شماره موبایلی را با کد `111111`** می‌پذیرد. پیامکی فرستاده نمی‌شود.
- **دوباره دیدن آموزش:** از «بازنشانی این دستگاه» پایین لابی استفاده کنید. حافظه‌ی محلی پاک می‌شود و بار بعد بازیکن کاملاً جدید است.
- **بازیکن دوم در همان مرورگر:** `?device=someone-else` را به آدرس اضافه کنید.
- **دریافت جایزه به‌عنوان مهمان:** بدون ورود یک مسابقه را ببرید. درخواست دریافت `PLATFORM_NOT_LINKED` برمی‌گرداند و شیت ورود باز می‌شود. بعد از ورود، دریافت خودش دوباره تلاش می‌کند.
- **خریدی که بعداً تمام می‌شود:** خرید را شروع کنید، پرداخت کنید، بازی را ببندید و دوباره باز کنید. لابی پرداخت‌های در انتظار را تطبیق می‌دهد.
- **لینک کمپین:** `…/example/?utm_source=wiki&utm_campaign=test` را باز کنید. آدرس بعد از بارگذاری تمیز است و برچسب‌ها هنگام ساخت حساب روی آن ذخیره می‌شوند.

## ساختار پروژه
```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
```

## میزبانی زیر یک مسیر
کلاینت فقط از آدرس‌های **نسبی** (`api/…`، `assets/…`) استفاده می‌کند، پس همین فایل‌ها هم در `/` و هم زیر یک پیشوند کار می‌کنند. روی سرور توسعه‌ی ویکی، nginx پیشوند `/example/` را برمی‌دارد و درخواست را به پروسه‌ی Node می‌فرستد:

```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;
}
```
