From f4295e780c11f3e6a0bfcd56a8c7934bb56adf7f Mon Sep 17 00:00:00 2001
From: Alireza Hassani
Date: Tue, 4 Aug 2026 11:09:21 +0330
Subject: [PATCH] Add partner SMS gateway via Gama SendQuick and document it.
Expose POST /public/sms/send with API key + domain allowlist for external backends like Balout, wire Meshkee OTP/message sends to Gama, and publish Partner SMS docs on /docs/website.
Co-authored-by: Cursor
---
.env.example | 10 +-
docs/PROJECT_CONTEXT.md | 8 +-
docs/website-api/AI_PROMPT.md | 1 +
...eshkee-Website-API.postman_collection.json | 32 ++++
docs/website-api/SMS.md | 85 +++++++++
docs/website-api/index.html | 12 ++
docs/website-api/openapi.json | 65 ++++++-
src/app.module.ts | 2 +
src/auth/sms.service.ts | 179 ++++++++++++++++--
src/public-sms/dto/send-public-sms.dto.ts | 19 ++
src/public-sms/public-sms.controller.ts | 20 ++
src/public-sms/public-sms.module.ts | 12 ++
src/public-sms/public-sms.service.ts | 73 +++++++
src/public-sms/sms-partner.guard.ts | 48 +++++
src/public-sms/sms-partners.util.ts | 41 ++++
src/redis/redis.service.ts | 16 ++
src/website-docs/static/AI_PROMPT.md | 1 +
...eshkee-Website-API.postman_collection.json | 32 ++++
src/website-docs/static/SMS.md | 85 +++++++++
src/website-docs/static/index.html | 12 ++
src/website-docs/static/openapi.json | 65 ++++++-
src/website-docs/website-docs.controller.ts | 1 +
22 files changed, 803 insertions(+), 16 deletions(-)
create mode 100644 docs/website-api/SMS.md
create mode 100644 src/public-sms/dto/send-public-sms.dto.ts
create mode 100644 src/public-sms/public-sms.controller.ts
create mode 100644 src/public-sms/public-sms.module.ts
create mode 100644 src/public-sms/public-sms.service.ts
create mode 100644 src/public-sms/sms-partner.guard.ts
create mode 100644 src/public-sms/sms-partners.util.ts
create mode 100644 src/website-docs/static/SMS.md
diff --git a/.env.example b/.env.example
index b690430..04aec4c 100644
--- a/.env.example
+++ b/.env.example
@@ -21,8 +21,16 @@ JWT_REFRESH_SECRET=change-me-refresh-secret-min-32-chars-long
JWT_ACCESS_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
-# SMS (set to true when SMS provider API is ready)
+# SMS — Gama (گاما) SendQuick via service shortcode
SMS_ENABLED=false
+SMS_GAMA_BASE_URL=https://sms.igama.ir/api/v1
+SMS_GAMA_USERNAME=
+SMS_GAMA_PASSWORD=
+SMS_GAMA_SOURCE_SERVICE=5000110005
+# Optional later: SMS_GAMA_SOURCE_ADVERTISE=500099000005
+# Partner gateway: domain:apiKey pairs, comma-separated (www. is stripped)
+# Example: SMS_PARTNERS=baloutpastry.com:replace-with-long-random-secret
+SMS_PARTNERS=
# Object storage (Parspack / S3-compatible)
# Bucket id is the Parspack account id. Object keys live under meshkee/...
diff --git a/docs/PROJECT_CONTEXT.md b/docs/PROJECT_CONTEXT.md
index d13b6a4..5d45035 100644
--- a/docs/PROJECT_CONTEXT.md
+++ b/docs/PROJECT_CONTEXT.md
@@ -1,7 +1,7 @@
# Meshkee CMS API — Project Context
> Living reference for developers and AI assistants working on this codebase.
-> Last updated: August 1, 2026
+> Last updated: August 4, 2026
## What This Project Is
@@ -79,6 +79,7 @@ src/
├── storage/ # S3 driver abstraction
├── website-docs/ # Public website API docs pack
├── invoices/ # Platform invoices + item templates (super-admin; business-ready schema)
+├── public-sms/ # Partner SMS gateway (API key + domain allowlist → Gama)
├── prisma/ # PrismaModule + PrismaService
├── redis/ # Redis client + OTP helpers
└── common/ # Shared interceptors (BigInt serialization)
@@ -361,6 +362,8 @@ Each resource typically has: `read`, `create`, `update`, `delete` (+ `publish` f
- Registration resolves tenant by `domain` → creates/links user → assigns `customer` role
- OTP stored in Redis (`otp:{cellNumber}`), 5-min TTL; disabled when `SMS_ENABLED=false`
- JWT payload: `sub`, `cellNumber`, `roles`, `dashboard`, `type`
+- SMS provider: Gama (`sms.igama.ir`) SendQuick via service shortcode (`SMS_GAMA_*`)
+- Partner gateway (external sites like Balout): `POST /api/v1/public/sms/send` with `X-Api-Key` + body `{ domain, to, message }`; partners configured in `SMS_PARTNERS` (`domain:apiKey` pairs). Rate limits: 30/partner/min and 5/destination/min. Not part of storefront website-api docs.
---
@@ -537,7 +540,7 @@ See `.env.example` for the full list. Key groups:
| Redis | `REDIS_URL`, `REDIS_HOST`, `REDIS_PORT` |
| API | `PORT` |
| JWT | `JWT_ACCESS_SECRET`, `JWT_REFRESH_SECRET`, `JWT_*_EXPIRES_IN` |
-| SMS | `SMS_ENABLED` |
+| SMS | `SMS_ENABLED`, `SMS_GAMA_BASE_URL`, `SMS_GAMA_USERNAME`, `SMS_GAMA_PASSWORD`, `SMS_GAMA_SOURCE_SERVICE`, `SMS_PARTNERS` |
| S3 | `S3_ENDPOINT`, `S3_BUCKET`, `S3_PUBLIC_URL`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` |
| Legacy MySQL (WillaEngine migrate) | `OLD_MYSQL_HOST`, `OLD_MYSQL_PORT`, `OLD_MYSQL_USER`, `OLD_MYSQL_PASSWORD`, `OLD_MYSQL_DATABASE` |
| Legacy S3 source (media copy) | `OLD_S3_ENDPOINT`, `OLD_S3_BUCKET`, `OLD_S3_PUBLIC_URL`, `OLD_S3_ACCESS_KEY_ID`, `OLD_S3_SECRET_ACCESS_KEY` |
@@ -563,6 +566,7 @@ See `.env.example` for the full list. Key groups:
- Category variations & technical forms
- Tenant resolution by domain
- RBAC with granular permissions
+- Partner SMS gateway (`POST /public/sms/send`) + Gama SendQuick integration
### Planned / partial
diff --git a/docs/website-api/AI_PROMPT.md b/docs/website-api/AI_PROMPT.md
index 686b918..5b0ddeb 100644
--- a/docs/website-api/AI_PROMPT.md
+++ b/docs/website-api/AI_PROMPT.md
@@ -26,6 +26,7 @@ You are building a **Meshkee business website (storefront)**. You must use the M
4. Customer register body must include `"domain": ""`.
5. Cell numbers are E.164 (`+98912...`).
6. Do not call dashboard/CMS routes (`/businesses/.../products` write APIs, media upload, domain-admin, etc.).
+7. **Partner SMS** (`POST /public/sms/send`) is for external partner backends with an issued `X-Api-Key` only — not for normal storefront UI. See https://api.meshkee.com/docs/website/SMS.md
### Typical bootstrap sequence
1. `GET /tenants/{domain}` → branding + `businessId`
diff --git a/docs/website-api/Meshkee-Website-API.postman_collection.json b/docs/website-api/Meshkee-Website-API.postman_collection.json
index 33df96f..b1f86c2 100644
--- a/docs/website-api/Meshkee-Website-API.postman_collection.json
+++ b/docs/website-api/Meshkee-Website-API.postman_collection.json
@@ -100,6 +100,10 @@
{
"key": "brandId",
"value": ""
+ },
+ {
+ "key": "smsApiKey",
+ "value": ""
}
],
"item": [
@@ -1669,6 +1673,34 @@
}
}
]
+ },
+ {
+ "name": "Partner SMS",
+ "description": "Server-to-server SMS for allowlisted partner domains. Set collection variable `smsApiKey`. Docs: https://api.meshkee.com/docs/website/SMS.md",
+ "item": [
+ {
+ "name": "Send SMS",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ },
+ {
+ "key": "X-Api-Key",
+ "value": "{{smsApiKey}}"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"domain\": \"baloutpastry.com\",\n \"to\": \"09127004945\",\n \"message\": \"سفارش شما به شماره ی ۱۲۱۱۳۲۲ اماده می باشد.\"\n}"
+ },
+ "url": "{{baseUrl}}/public/sms/send",
+ "description": "Requires allowlisted domain + matching API key. Not for browser JS."
+ }
+ }
+ ]
}
]
}
diff --git a/docs/website-api/SMS.md b/docs/website-api/SMS.md
new file mode 100644
index 0000000..f7405ae
--- /dev/null
+++ b/docs/website-api/SMS.md
@@ -0,0 +1,85 @@
+# Partner SMS gateway
+
+Server-to-server SMS via Meshkee → Gama (گاما). Use this when a **non-Meshkee** (or partner) backend needs to send SMS through the shared Meshkee account.
+
+**Not for browser/storefront JavaScript.** Never put the API key in frontend code.
+
+Hub: https://api.meshkee.com/docs/website
+Endpoint docs also in OpenAPI / Postman under **Partner SMS**.
+
+---
+
+## Endpoint
+
+```http
+POST https://api.meshkee.com/api/v1/public/sms/send
+Content-Type: application/json
+X-Api-Key:
+```
+
+### Body
+
+| Field | Required | Description |
+|-------|----------|-------------|
+| `domain` | yes | Allowlisted partner apex, e.g. `baloutpastry.com` (`www.` is stripped) |
+| `to` | yes | Mobile: `09…`, `9…`, `+989…`, or `989…` |
+| `message` | yes | Free text, max 700 characters |
+
+### Example (Balout)
+
+```bash
+curl -sS -X POST 'https://api.meshkee.com/api/v1/public/sms/send' \
+ -H 'Content-Type: application/json' \
+ -H 'X-Api-Key: ' \
+ -d '{
+ "domain": "baloutpastry.com",
+ "to": "09127004945",
+ "message": "سفارش شما به شماره ی ۱۲۱۱۳۲۲ اماده می باشد."
+ }'
+```
+
+### Success (200)
+
+```json
+{
+ "success": true,
+ "serverId": "1136923081051406337"
+}
+```
+
+`serverId` is the Gama message id (delivery tracking).
+
+### Errors
+
+| HTTP | Meaning |
+|------|---------|
+| 400 | Invalid phone or empty/too-long message |
+| 401 | Missing/invalid `X-Api-Key` or domain not allowlisted |
+| 429 | Rate limit: **30**/partner/minute or **5**/destination/minute |
+| 503 | SMS disabled, missing provider config, or Gama unreachable |
+
+---
+
+## Auth model
+
+1. Meshkee configures `SMS_PARTNERS=domain:apiKey,...` on the API server.
+2. Partner backend sends `X-Api-Key` + matching `domain` in the JSON body.
+3. Key must match that domain (timing-safe compare). `www.baloutpastry.com` normalizes to `baloutpastry.com`.
+
+First allowlisted partner: **baloutpastry.com**.
+
+---
+
+## Sender line (v1)
+
+Uses the **service** shortcode only (`SendQuick`). Advertising / bulk / OTP pattern APIs are not exposed yet.
+
+---
+
+## Rules for partner backends
+
+1. Call from your **server** only (Balout API → Meshkee API).
+2. Keep the API key in server env / secrets — never in the website frontend.
+3. Prefer short transactional messages (order ready, OTP-style text, etc.).
+4. Respect rate limits; backoff on `429`.
+5. Meshkee websites that already use customer auth OTP go through Meshkee’s own auth/SMS path — they do **not** need this partner endpoint.
diff --git a/docs/website-api/index.html b/docs/website-api/index.html
index 9191fb0..d49338c 100644
--- a/docs/website-api/index.html
+++ b/docs/website-api/index.html
@@ -77,6 +77,7 @@
OpenAPI JSON
Download Postman
AI prompt
+ Partner SMS
Base URL
@@ -104,6 +105,17 @@
Postman → Import → Link → paste
https://api.meshkee.com/docs/website/Meshkee-Website-API.postman_collection.json
+
+ Partner SMS gateway
+
+ External backends (e.g. Balout) can send transactional SMS through Meshkee → Gama.
+ Server-to-server only — API key per allowlisted domain. See
+ SMS.md.
+
+
+
POST /api/v1/public/sms/send
+
Header X-Api-Key + body { domain, to, message }
+