# Notify me when available (أعلمني عند التوفر) — mobile app contract

When the chosen variant (or a product without variants) **cannot be bought**, the app shows a
**"أعلمني عند التوفر"** button instead of "add to cart". The customer may leave an optional message.
When the product is available again, the backend sends them a **WhatsApp message with a link to the
product** and an **app push**, once. The website does the same, with the same rules (one shared
backend service), so a request made on the site shows in the app and the other way round.

- Signed-in customers only: a guest gets `401` → open the sign-in screen, then retry.
- One request per customer per product variant. Asking again only updates the message.
- A request is accepted only while the product is on sale and **cannot** be bought now.
- Nothing is sent while the product is hidden (unpublished / not approved / seller not verified),
  even if its stock came back; the message goes when it is shown again.

All endpoints need the usual headers (`System-Key`, `Authorization: Bearer <sanctum token>`).
Base: `/api/v2`. **No existing field changed**; the app only reads the new keys below.

## 1. When to show the button

### Product details — `GET products/{slug}/{user_id}` (existing), two new keys

```json
{
  "in_stock": 0,
  "stock_alert_variants": ["Black-XL"]
}
```

| key | meaning |
|---|---|
| `in_stock` | `1` if **any** variant can be ordered now (quantity ≥ the product's minimum order), else `0`. Digital products are always `1`. Use it for a product without variants. |
| `stock_alert_variants` | the variants this customer is already waiting for (`""` = the whole product). Empty for a guest. |

> `current_stock` is unchanged and is still the **first** variant's quantity only — do not use it to decide availability.

### Variant price — `POST products/variant/price` (existing), one new key

The existing `in_stock` (per chosen variant) decides the button: `in_stock == 0` and `digital == 0`
→ show "أعلمني عند التوفر" (or "رح نعلمك أول ما يتوفر" when `stock_alert == true`).

```json
{ "result": true, "data": { "variant": "Black-XL", "in_stock": 0, "stock_alert": true, "...": "unchanged" } }
```

| key | meaning |
|---|---|
| `stock_alert` | `true` when this customer already asked for this variant (or the whole product). Always `false` while `in_stock == 1`. `false` for a guest. |

Send the request with the **same `variant` string** this endpoint returns (`data.variant`, e.g. `"Black-XL"`).
For a product without variants send `""`.

## 2. Ask — `POST stock-alerts`

```json
{ "product_id": 812, "variant": "Black-XL", "message": "بدي اللون الأسود إذا رجع" }
```

`variant` and `message` are optional (`message` up to 500 characters, emoji allowed).

```json
200 { "result": true, "code": "created", "message": "تمام، رح نعلمك أول ما يتوفر 👍", "alert_id": 31 }
200 { "result": true, "code": "updated", "message": "تمام، حدّثنا طلبك ورح نعلمك أول ما يتوفر", "alert_id": 31 }
```

Refusals (`result: false`): show `message` to the customer.

| HTTP | `code` | when |
|---|---|---|
| 401 | `login` | no / expired token → sign in |
| 422 | `available` | it can be bought now → refresh the product and show "add to cart" |
| 422 | `bad_variant` | that variant does not exist for this product |
| 422 | `not_found` | no such product, or not on sale |
| 422 | `not_ready` | the feature is not installed on this server yet |
| 429 | — | more than 30 requests a minute |

## 3. Withdraw — `POST stock-alerts/cancel`

```json
{ "product_id": 812, "variant": "Black-XL" }
```

Without `variant`, every request of the customer for that product is withdrawn.

```json
200 { "result": true, "code": "cancelled", "message": "ألغينا التنبيه", "alert_id": null }
```

## 4. My requests — `GET stock-alerts`

The customer's requests still waiting (or about to be sent), newest first, up to 100.

```json
{
  "result": true,
  "data": [
    {
      "id": 31,
      "product_id": 812,
      "product_name": "سماعات Xiaomi Redmi Buds 8 Active",
      "product_slug": "xiaomi-redmi-buds-8-active",
      "thumbnail_image": "https://kapitano.shop/public/uploads/all/....webp",
      "variant": "Black-XL",
      "message": "بدي اللون الأسود إذا رجع",
      "status": "waiting",
      "created_at": "2026-10-12T10:42:00+03:00"
    }
  ]
}
```

`status`: `waiting` (still unavailable) or `queued` (back, the message goes at the next round: 09:00 or 21:00).

## 5. The push (FCM)

Sent once when the product is back, with the WhatsApp message:

```json
{
  "notification": { "title": "رجع متوفر 🎉", "body": "«سماعات Xiaomi Redmi Buds 8 Active» يلي طلبت نعلمك فيه رجع متوفر بكابيتانو" },
  "data": {
    "item_type": "back_in_stock",
    "item_type_id": "812",
    "link": "https://kapitano.shop/product/xiaomi-redmi-buds-8-active",
    "click_action": "FLUTTER_NOTIFICATION_CLICK"
  }
}
```

On tap: open the product. `item_type_id` is the **product id**; `link` is its web page (its last
path segment is the slug used by `products/{slug}/{user_id}`).

## 6. Checklist for the app

1. Product page: after every `products/variant/price` answer, show the button when `in_stock == 0 && digital == 0`;
   for a product without variants use `in_stock` from the details.
2. Button → (guest: sign in) → small sheet with an optional message → `POST stock-alerts`.
3. Already asked (`stock_alert == true`, or the variant in `stock_alert_variants`) → "رح نعلمك أول ما يتوفر" + "إلغاء" → `POST stock-alerts/cancel`.
4. Handle the push `item_type = back_in_stock` → open product `item_type_id`.
5. Optional: a "my alerts" list from `GET stock-alerts`.
6. **A sold-out colour or size must stay selectable** (struck through, but tappable). If the app disables it,
   the customer who wants that colour can never reach the button. Selecting it calls `products/variant/price`
   as usual, gets `in_stock == 0`, hides "add to cart" / "buy now" and shows "أعلمني عند التوفر" (the website
   does exactly this since 2026-10-12).
