Files
CRM_Montzar/docs/MONTACT_CRM_INTEGRATION.md
T

439 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# راهنمای اتصال CRM (MONTACT) به Montify — نسخه کامل v2
## ۱. خلاصه و دیاگرام جریان
این سند مشخصات اتصال سامانه **Montact CRM** به زیرسیستم‌های فروشگاهی (مانند Montify) را تعریف می‌کند. هر فروشگاه یک `Base URL` و یک `Token` اختصاصی دارد.
```
[فروشنده/Store] → [پنل Montify] → [صدور Token] → [سرور CRM]
│
▼
GET {Base URL}/montact/customers
│
▼
پاسخ JSON (لیست مشتریان)
│
▼
GET {Base URL}/montact/customers/:id/orders
│
▼
پاسخ JSON (سفارش‌های مشتری)
```
## ۲. دریافت اطلاعات اتصال از فروشنده
1. فروشنده وارد پنل Montify می‌شود.
2. از مسیر «تنظیمات → یکپارچه‌سازی‌ها → CRM» وارد بخش Montact می‌شود.
3. دکمه «صدور Token» را می‌زند و مقادیر زیر را در اختیار تیم CRM قرار می‌دهد:
| فیلد | مقدار نمونه | توضیح |
|---|---|---|
| Base URL | `https://store1.montify.com/api/v1` | آدرس پایه API فروشگاه |
| Token | `mk_live_8f3a1c9d2e...` | توکن احراز هویت Bearer |
| Store ID | `b2f1e4a0-...` | شناسه فروشگاه |
> **نکته:** توکن‌ها می‌توانند منقضی شوند (`expires_at`). در صورت دریافت خطای ۴۰۱، باید توکن جدید از فروشنده درخواست شود. توکن هرگز نباید در فرانت‌اند ذخیره یا افشا شود؛ تنها باید در سرور CRM نگهداری گردد.
## ۳. احراز هویت
تمام درخواست‌ها باید هدرهای زیر را داشته باشند:
```http
Authorization: Bearer {Token}
Content-Type: application/json
Accept: application/json
```
## ۴. فهرست کامل APIها
| Method | Path | توضیح |
|---|---|---|
| GET | `/montact/customers` | لیست مشتریان (پشتیبانی از `page`, `limit`, `updated_after`) |
| POST | `/montact/customers` | ثبت/همگام‌سازی مشتری از سمت CRM (اختیاری) |
| GET | `/montact/customers/:id` | جزئیات کامل یک مشتری |
| GET | `/montact/customers/:id/orders` | لیست سفارش‌های مشتری (پشتیبانی از `updated_after`) |
| POST | `/montact/customers/:id/orders` | ثبت سفارش از CRM (اختیاری، در صورت فعال بودن) |
| GET | `/montact/customers/:id/addresses` | لیست آدرس‌های مشتری |
| GET | `/montact/customers/:id/transactions` | لیست تراکنش‌های مالی مشتری |
| GET | `/montact/customers/:id/engagement` | داده‌های تعاملی (`saved`, `likes`, `comments`) |
| GET | `/montact/orders/:orderId` | جزئیات یک سفارش خاص |
| POST | `/montact/webhooks/register` | ثبت Webhook برای رویدادهای بلادرنگ (فاز ۳) |
| GET | `/montact/webhooks/:id` | مشاهده جزئیات یک Webhook ثبت‌شده |
| DELETE | `/montact/webhooks/:id` | حذف Webhook |
> پارامتر `updated_after` یک مقدار ISO 8601 است (مثلاً `2026-07-01T00:00:00Z`) و فقط رکوردهای تغییر یافته بعد از آن زمان را برمی‌گرداند (برای Incremental Sync).
### نمونه‌های curl
```bash
BASE="https://store1.montify.com/api/v1"
TOKEN="mk_live_8f3a1c9d2e..."
# لیست مشتریان
curl -X GET "$BASE/montact/customers?page=1&limit=50" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json"
# لیست مشتریان تغییریافته بعد از یک زمان خاص (Incremental Sync)
curl -X GET "$BASE/montact/customers?updated_after=2026-07-01T00:00:00Z" \
-H "Authorization: Bearer $TOKEN"
# جزئیات یک مشتری
curl -X GET "$BASE/montact/customers/123" \
-H "Authorization: Bearer $TOKEN"
# سفارش‌های یک مشتری
curl -X GET "$BASE/montact/customers/123/orders?updated_after=2026-07-01T00:00:00Z" \
-H "Authorization: Bearer $TOKEN"
# آدرس‌های مشتری
curl -X GET "$BASE/montact/customers/123/addresses" \
-H "Authorization: Bearer $TOKEN"
# تراکنش‌های مشتری
curl -X GET "$BASE/montact/customers/123/transactions" \
-H "Authorization: Bearer $TOKEN"
# داده‌های تعاملی مشتری
curl -X GET "$BASE/montact/customers/123/engagement" \
-H "Authorization: Bearer $TOKEN"
# جزئیات یک سفارش خاص
curl -X GET "$BASE/montact/orders/4567" \
-H "Authorization: Bearer $TOKEN"
# ثبت Webhook (فاز ۳)
curl -X POST "$BASE/montact/webhooks/register" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"url":"https://crm.montact.com/webhooks/montify","events":["order.created","order.paid"]}'
```
## ۵. ساختار پاسخ کامل
### ۵.۱ فیلدهای مشتری (Customer)
| فیلد | نوع | توضیح |
|---|---|---|
| id | integer | شناسه مشتری |
| name | string | نام کامل |
| first_name | string | نام |
| last_name | string | نام خانوادگی |
| phone | string | شماره موبایل |
| email | string\|null | ایمیل |
| username | string | نام کاربری |
| avatar | string\|null | URL آواتار |
| birthday | date\|null | تاریخ تولد |
| gender | string\|null | جنسیت |
| type | string | نوع مشتری (حقیقی/حقوقی) |
| store_id | integer | شناسه فروشگاه |
| created_at | datetime | تاریخ ایجاد |
| updated_at | datetime | آخرین بروزرسانی |
| description | string\|null | توضیحات |
| bio | string\|null | بیوگرافی |
| job | string\|null | شغل |
| cash_balance | number | موجودی نقدی |
| gold_balance | number | موجودی طلا (گرم) |
| orders | array | لیست سفارش‌ها |
| summary | object | خلاصه آماری مشتری |
| addresses | array | لیست آدرس‌ها |
| transactions | array | لیست تراکنش‌ها |
| engagement | object | داده‌های تعاملی |
### ۵.۲ فیلدهای سفارش (Order)
| فیلد | نوع | توضیح |
|---|---|---|
| id | integer | شناسه سفارش |
| user_id | integer | شناسه مشتری |
| store_id | integer | شناسه فروشگاه |
| code | string | کد پیگیری سفارش |
| sts | string | وضعیت سفارش |
| payment_sts | string | وضعیت پرداخت |
| payment_type | string | نوع پرداخت |
| total_price | number | قیمت کل |
| discount_total_price | number | قیمت پس از تخفیف |
| total_discounts | number | مجموع تخفیف‌ها |
| total_tax | number | مجموع مالیات |
| total_qty | integer | تعداد کل اقلام |
| profit | number | سود |
| percent_profit | number | درصد سود |
| sum_gold | number | مجموع وزن طلا |
| sum_silver | number | مجموع وزن نقره |
| address | object | آدرس ارسال |
| address_id | integer | شناسه آدرس |
| note | string\|null | یادداشت |
| cancel_reason | string\|null | دلیل لغو |
| cancelled_at | datetime\|null | تاریخ لغو |
| closed_at | datetime\|null | تاریخ بستن سفارش |
| transaction_id | integer\|null | شناسه تراکنش مرتبط |
| code_discount | string\|null | کد تخفیف |
| discount_code_id | integer\|null | شناسه کد تخفیف |
| created_at | datetime | تاریخ ایجاد |
| updated_at | datetime | آخرین بروزرسانی |
| deleted_at | datetime\|null | تاریخ حذف نرم |
| order_items | array | اقلام سفارش |
| user | object | اطلاعات خلاصه مشتری |
| store | object | اطلاعات خلاصه فروشگاه |
### ۵.۳ اقلام سفارش (Order Items)
| فیلد | نوع | توضیح |
|---|---|---|
| id | integer | شناسه قلم سفارش |
| order_id | integer | شناسه سفارش |
| product_id | integer | شناسه محصول |
| product_name | string | نام محصول |
| qty | integer | تعداد |
| price | number | قیمت واحد |
| weight | number | وزن (گرم) |
| total_price | number | قیمت کل قلم |
### ۵.۴ آدرس‌ها (Addresses)
| فیلد | نوع | توضیح |
|---|---|---|
| id | integer | شناسه آدرس |
| user_id | integer | شناسه مشتری |
| province | string | استان |
| city | string | شهر |
| address | string | متن آدرس |
| postal_code | string | کد پستی |
| receiver_name | string | نام گیرنده |
| receiver_phone | string | شماره گیرنده |
| is_default | boolean | آدرس پیش‌فرض |
### ۵.۵ تراکنش‌ها (Transactions)
| فیلد | نوع | توضیح |
|---|---|---|
| id | integer | شناسه تراکنش |
| user_id | integer | شناسه مشتری |
| type | string | نوع تراکنش (واریز/برداشت) |
| amount | number | مقدار |
| currency | string | واحد (ریال/گرم طلا) |
| status | string | وضعیت تراکنش |
| reference_code | string | کد پیگیری بانکی |
| created_at | datetime | تاریخ ثبت |
### ۵.۶ داده‌های تعاملی (Engagement)
مسیر: `GET /montact/customers/:id/engagement`
```json
{
"saved": [
{ "product_id": 55, "product_name": "سکه بهار آزادی", "saved_at": "2026-06-10T11:22:00Z" }
],
"likes": [
{ "product_id": 78, "liked_at": "2026-06-15T09:00:00Z" }
],
"comments": [
{
"id": 301,
"product_id": 78,
"text": "کیفیت عالی بود",
"rating": 5,
"created_at": "2026-06-16T08:30:00Z"
}
]
}
```
### ۵.۷ نمونه پاسخ JSON کامل لیست مشتریان
```json
{
"data": [
{
"id": 123,
"name": "علی رضایی",
"first_name": "علی",
"last_name": "رضایی",
"phone": "09121234567",
"email": "ali@example.com",
"username": "ali_r",
"avatar": "https://cdn.montify.com/avatars/123.png",
"birthday": "1990-05-12",
"gender": "male",
"type": "individual",
"store_id": 10,
"created_at": "2025-01-10T08:00:00Z",
"updated_at": "2026-07-01T10:15:00Z",
"description": null,
"bio": null,
"job": "مهندس",
"cash_balance": 1500000,
"gold_balance": 2.35,
"orders": [],
"summary": { "total_orders": 12, "total_spent": 85000000 },
"addresses": [],
"transactions": [],
"engagement": { "saved": [], "likes": [], "comments": [] }
}
],
"meta": {
"current_page": 1,
"per_page": 50,
"total": 532,
"last_page": 11
}
}
```
## ۶. قوانین نگاشت پیشنهادی در CRM
| فیلد Montify | فیلد معادل در CRM |
|---|---|
| id | external_customer_id |
| name | full_name |
| phone | mobile |
| cash_balance | wallet_balance |
| gold_balance | gold_wallet_balance |
| orders[].code | order_reference |
| orders[].sts | order_status |
**استراتژی Sync گام به گام:**
1. دریافت لیست مشتریان صفحه‌بندی‌شده و Upsert در جدول مشتریان CRM بر اساس `id`.
2. برای هر مشتری، دریافت سفارش‌ها، آدرس‌ها و تراکنش‌ها به‌صورت جداگانه و Upsert در جداول مرتبط.
3. ذخیره `updated_at` هر رکورد برای استفاده در Sync افزایشی بعدی.
## ۷. استراتژی Sync سه‌فازی
### فاز ۱ — Polling ساده
- فراخوانی دوره‌ای `/montact/customers` با `page` و `limit`.
- فاصله پیشنهادی: هر ۵ تا ۱۵ دقیقه (بسته به حجم داده فروشگاه).
- در صورت دریافت `429 Too Many Requests`، باید بر اساس هدر `Retry-After` صبر و تلاش مجدد شود.
### فاز ۲ — Incremental Sync
- استفاده از `updated_after=<ISO8601>` روی `/montact/customers` و `/montact/customers/:id/orders`.
- CRM باید مقدار `last_sync_timestamp` را پس از هر Sync موفق ذخیره کند و در فراخوانی بعدی به‌عنوان `updated_after` ارسال کند.
```bash
curl -X GET "$BASE/montact/customers?updated_after=2026-07-10T12:00:00Z" \
-H "Authorization: Bearer $TOKEN"
```
### فاز ۳ — Webhook (Push-based، بلادرنگ)
برای دریافت رویدادها بدون تأخیر Polling، CRM باید یک Endpoint عمومی HTTPS در اختیار Montify قرار دهد:
```bash
curl -X POST "$BASE/montact/webhooks/register" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://crm.montact.com/webhooks/montify",
"events": ["customer.created", "customer.updated", "order.created", "order.updated", "order.paid", "order.cancelled"]
}'
```
**رویدادهای پشتیبانی‌شده:**
| رویداد | توضیح |
|---|---|
| customer.created | ایجاد مشتری جدید |
| customer.updated | بروزرسانی اطلاعات مشتری |
| order.created | ثبت سفارش جدید |
| order.updated | تغییر در سفارش |
| order.paid | پرداخت سفارش |
| order.cancelled | لغو سفارش |
**نمونه Payload برای `order.created`:**
```json
{
"event": "order.created",
"delivery_id": "d2f1c9e0-84a3-4b7f-9c1e-3a2b1c0d9e8f",
"store_id": 10,
"data": {
"id": 4567,
"user_id": 123,
"code": "ORD-2026-4567",
"sts": "pending",
"payment_sts": "unpaid",
"total_price": 12500000,
"total_qty": 2,
"created_at": "2026-07-10T12:05:00Z"
}
}
```
**هدرهای ارسالی Webhook:**
```http
X-Montact-Signature: sha256=3b4f9a1c7e8d2f...
X-Montact-Event: order.created
X-Montact-Delivery-Id: d2f1c9e0-84a3-4b7f-9c1e-3a2b1c0d9e8f
Content-Type: application/json
```
**وریفای امضا (Node.js):**
```javascript
const crypto = require('crypto');
function verifySignature(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const received = signatureHeader.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(received, 'hex')
);
}
```
**Retry Policy:**
- در صورت عدم دریافت پاسخ `2xx` ظرف ۱۰ ثانیه، تلاش مجدد با backoff نمایی انجام می‌شود.
- فاصله تلاش‌ها: `1s → 5s → 15s → 60s → 300s` (حداکثر ۵ بار).
- CRM باید سریعاً `200 OK` برگرداند و پردازش سنگین را به‌صورت async انجام دهد.
- Idempotency بر اساس `X-Montact-Delivery-Id` باید رعایت شود تا پردازش تکراری رخ ندهد.
## ۸. کدهای خطا
| کد HTTP | معنی | اقدام پیشنهادی |
|---|---|---|
| 200 | موفق | - |
| 400 | درخواست نامعتبر | بررسی پارامترها |
| 401 | توکن نامعتبر یا منقضی | درخواست توکن جدید از فروشنده |
| 403 | عدم دسترسی به منبع | بررسی scope توکن |
| 404 | منبع یافت نشد | بررسی شناسه ارسالی |
| 429 | تعداد درخواست بیش از حد مجاز | توجه به هدر `Retry-After` و کاهش نرخ درخواست |
| 500 | خطای سرور | تلاش مجدد بعد از فاصله زمانی |
## ۹. محدودیت‌ها و رفتار فعلی
- API برای داده‌های اصلی (مشتری، سفارش، آدرس، تراکنش) صرفاً **خواندنی (Read-only)** است؛ تنها استثنا ثبت/حذف Webhook است.
- فیلدهای حساس (مانند رمز عبور، هش‌های داخلی) از پاسخ‌ها حذف شده‌اند.
- CORS برای دامنه‌های مجاز CRM فعال است.
- نرخ مجاز درخواست پیشنهادی: **۱۰۰ درخواست در دقیقه به ازای هر Token**.
## ۱۰. چک‌لیست اتصال برنامه‌نویس CRM
- [ ] دریافت `Base URL`، `Token` و `Store ID` از فروشنده
- [ ] تست احراز هویت با `GET /montact/customers`
- [ ] پیاده‌سازی Polling فاز ۱
- [ ] پیاده‌سازی Incremental Sync با `updated_after` (فاز ۲)
- [ ] ثبت Webhook Endpoint و تست دریافت رویداد (فاز ۳)
- [ ] پیاده‌سازی وریفای امضای HMAC-SHA256
- [ ] تست Idempotency بر اساس `X-Montact-Delivery-Id`
- [ ] مدیریت خطاهای ۴۰۱/۴۲۹ و Retry مناسب
- [ ] ذخیره امن Token در سمت سرور (نه فرانت‌اند)
## ۱۱. پشتیبانی
برای ارسال تیکت پشتیبانی، اطلاعات زیر لازم است:
- `Store ID`
- زمان دقیق درخواست (UTC)
- کد خطای دریافتی
- `X-Montact-Delivery-Id` (در صورت مرتبط بودن به Webhook)
- نمونه Request/Response (بدون افشای Token کامل)