اتصال نرمافزارها، سامانه حسابداری و CRM به دادههای استخراجشدهی چرتکه
چرتکه فاکتورها و پروندههای خسارت را به داده ساختاریافته تبدیل میکند. بخش «تنظیمات توسعهدهنده» در /app/settings دو راه برای اتصال دادههای سازمان شما به سایر سیستمها فراهم میکند:
نرمافزار شما در هر زمان میتواند لیست اسناد و آمار مصرف را با یک درخواست HTTP دریافت کند؛ مناسب داشبوردها، گزارشهای دورهای و همگامسازی.
به محض ذخیره یک فاکتور جدید، چرتکه بلافاصله آن را به آدرس شما POST میکند؛ مناسب ثبت خودکار در نرمافزار حسابداری و CRM.
چرتکه با ترکیب «توکن API + وبهوک» میتواند داده فاکتور را مستقیم به نرمافزار حسابداری یا CRM سازمان شما منتقل کند. آدرس پایه، روش احراز هویت و بدنه درخواست ثبت فاکتور در هر نرمافزار متفاوت است؛ از فهرست زیر راهنمای نرمافزار خودتان را دنبال کنید:
GET /api/v1/documents برای تولید خروجی قابلدرج (Excel/CSV) استفاده کنید.هلو یک وبسرویس REST را روی همان سرور نصب نرمافزار (پیشفرض پورت 8080) فعال میکند. ابتدا با Login توکن Bearer میگیرید و بقیه متدها را با آن صدا میزنید:
| متد | Endpoint | توضیح |
|---|---|---|
| POST | {Address}/Login | احراز هویت؛ نتیجه Login.State و Login.Token (Bearer). |
| POST | {Address}/Invoice/Invoice | ثبت فاکتور؛ Type: 1 فروش، 2 برگشت فروش، 3 خرید، 4 برگشت خرید. |
| POST | {Address}/Invoice/PreInvoice | ثبت پیشفاکتور (نوعها مثل Invoice). |
| POST | {Address}/Invoice/Order | ثبت سفارش (نوعها مثل Invoice). |
| GET | {Address}/Invoice | دریافت فاکتورها با فیلتر. |
| POST | {Address}/Customer | ثبت مشتری. |
| GET | {Address}/Customer | دریافت مشتریها. |
| POST | {Address}/Product | ثبت کالا/خدمت. |
| GET | {Address}/Product | دریافت کالاها. |
| POST | {Address}/webhook/subscribe | وبهوک هلو برای گزارش رویدادها (tables: customer / product / mainGroup / sideGroup / resInvoice). |
# آدرس پایه هلو: http://[IP]:8080/TncHoloo/api (نام دیتابیس هلو را از پیکربندی بگیرید)
# ۱) لاگین — رمز عبور را Base64 کنید
curl -X POST "http://[IP-Server-Hello]:8080/TncHoloo/api/Login" \
-H "Content-Type: application/json" \
-d '{"userinfo":{"username":"admin","userpass":"<Base64 رمز>","dbname":"Holoo1"}}'
# پاسخ موفق:
# {"Login":{"State":"True","Token":"Bearer <token>","ErrorCode":0,"Error":""}}
# ۲) برای همه متدها لازم است:
# Authorization: Bearer <token>
# Content-Type: application/json
// وبهوک چرتکه ← درج فاکتور در هلو (Node.js / Next.js)
export async function POST(req) {
// ۱) تأیید امضای X-Chortke-Signature — کد بخش «وبهوک» بالا
const payload = await req.json();
if (payload.event !== "document.created") return Response.json({ ok: true });
const d = payload.data;
const holo = "http://[IP-Server-Hello]:8080/TncHoloo/api";
const token = "<Token-از-لاگین>"; // با لاگین گرفته و کش کنید
const body = {
invoiceinfo: [{
Type: "3", // 1=فروش 2=برگشت از فروش 3=خرید 4=برگشت از خرید
customererpcode: d.buyerErpCode, // ErpCode طرفحساب ثبتشده در هلو
date: "2026-09-16", // میلادی YYYY-MM-DD (تاریخ شمسی را تبدیل کنید)
time: "10:30",
Cash: Number(d.finalAmount),
Discount: 0,
detailinfo: {
ProductErpCode: d.itemErpCode, // ErpCode کالای ثبتشده در هلو
few: Number(d.quantity ?? 1),
price: Number(d.finalAmount),
levy: 0,
scot: 0,
},
}],
};
await fetch(`${holo}/Invoice/Invoice`, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
body: JSON.stringify(body),
});
return Response.json({ ok: true });
}
POST http://[IP]:8080/TncHoloo/api/webhook/subscribe
Content-Type: application/json
{"subscriptions":[{"url":"https://server.com/hook","tablename":"product","apikey":"<کلید ثابت>"}]}
# tablename مجاز: customer | product | mainGroup | sideGroup | resInvoice
# اعلان دریافتی: {"Dbname":"Holoo1","Table":"product","operation":"create|update|delete","changedfields":"[...]"}
سپیدار سیستم یک وبسرویس محلی (پیشفرض http://localhost:7373) فراهم میکند. پس از ثبت دستگاه (کلید RSA) و لاگین (JWT)، فاکتورها را پست میکنید:
| متد | Endpoint | توضیح |
|---|---|---|
| POST | /api/Devices/Register | ثبت یکبار دستگاه؛ پاسخ شامل کلید عمومی (XML/RSA) برای رمزنگاری مقادیر. |
| POST | /api/users/login | لاگین؛ Credentials: UserName + PasswordHash (MD5). |
| POST | /api/invoices/ | ثبت فاکتور فروش با GUID و Items. |
| GET | /api/invoices/:id | دریافت جزئیات فاکتور. |
# آدرس پایه سپیدار: http://localhost:7373 (از تنظیمات «حالت سرویسدهنده» قابل تغییر است)
# ۱) ثبت دستگاه (یکبار) — پاسخ شامل کلید عمومی (قالب XML / RSA) برای رمزنگاری است
curl -X POST "http://[IP-Sepidar]:7373/api/Devices/Register" \
-H "Content-Type: application/json" \
-d '{"DeviceName":"Chortke"}'
# ۲) لاگین
# برای هر درخواست یک GUID جدید بهعنوان ArbitraryCode بسازید:
# EncArbitraryCode = Base64( RSA-PKCS1-v1_5(ArbitraryCode, کلید عمومی) )
curl -X POST "http://[IP-Sepidar]:7373/api/users/login" \
-H "Authorization: Bearer <JWT>" \
-H "GenerationVersion: 1" \
-H "IntegrationID: <شناسه-دستگاه-Registered>" \
-H "ArbitraryCode: <GUID-جدید>" \
-H "EncArbitraryCode: <Base64-RSA>" \
-H "Content-Type: application/json" \
-d '{"UserName":"<نام کاربری>","PasswordHash":"<MD5-رمز>"}'
# ۳) برای همه متدها: Authorization: Bearer JWT + GenerationVersion + IntegrationID + ArbitraryCode + EncArbitraryCode
// وبهوک چرتکه ← ثبت فاکتور در سپیدار (Node.js / Next.js)
// توابع Register/Login و encryptRSA را طبق مستندات رسمی سپیدار پیاده کنید.
export async function POST(req) {
// ۱) تأیید امضای X-Chortke-Signature — کد بخش «وبهوک» بالا
const payload = await req.json();
if (payload.event !== "document.created") return Response.json({ ok: true });
const d = payload.data;
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer <JWT-از-لاگین>",
GenerationVersion: "1",
IntegrationID: "<IntegrationID-از-Register>",
ArbitraryCode: crypto.randomUUID(),
EncArbitraryCode: encryptRSA(crypto.randomUUID()), // RSA PKCS#1 v1.5 + Base64
};
const body = {
GUID: crypto.randomUUID(), // جلوگیری از ثبت تکراری (Idempotency)
CustomerRef: d.customerRef, // شناسه مشتری ثبتشده در سپیدار
SaleTypeRef: d.saleTypeRef ?? 1,
DiscountOnCustomer: 0,
Price: Number(d.finalAmount), // مبلغ ناخالص
Discount: 0,
Tax: 0,
Duty: 0,
Addition: 0,
Items: [{
ItemRef: d.itemRef, // شناسه کالا در سپیدار
StockRef: 0,
Quantity: Number(d.quantity ?? 1),
SecondaryQuantity: 0,
Fee: Number(d.finalAmount),
Description: d.invoiceNumber,
}],
};
await fetch("http://[IP-Sepidar]:7373/api/invoices/", {
method: "POST",
headers,
body: JSON.stringify(body),
});
return Response.json({ ok: true });
}
GenerationVersion، IntegrationID،ArbitraryCode و EncArbitraryCode نیاز دارند وArbitraryCode باید برای هر درخواست جدید ساخته شود. بدون لایسنس ماژول وبسرویس، سرور پاسخ نمیدهد.دیدار یک CRM ابری با API عمومی است؛ کلید API را از تنظیمات توسعهدهنده دیدار بگیرید و در کوئری بفرستید:
| متد | Endpoint | توضیح |
|---|---|---|
| POST | /api/AccountingInvoice/Create | ایجاد فاکتور حسابداری؛ نیازمند SellerInfo.BizDomainId و Items. |
| POST | /api/AccountingInvoice/List | فهرست فاکتورهای حسابداری. |
| POST | /api/AccountingInvoice/Delete | حذف فاکتور. |
| GET | /api/Product/GetProducts | دریافت کالاها برای یافتن ProductId. |
| GET | /api/Product/GetProductsByCode | دریافت کالا با کد. |
// وبهوک چرتکه ← ایجاد فاکتور در دیدار (Node.js / Next.js)
const API_KEY = "<کلید-API-دیدار>";
export async function POST(req) {
// تأیید امضا — کد بخش «وبهوک» بالا
const payload = await req.json();
if (payload.event !== "document.created") return Response.json({ ok: true });
const d = payload.data;
const body = {
Items: [{
ProductId: d.productId, // از Search Products / Get Products By Code
Title: d.title ?? "فاکتور چرتکه",
Quantity: Number(d.quantity ?? 1),
Unit: d.unit ?? "عدد",
UnitPrice: Number(d.finalAmount),
Discount: 0,
DiscountPercent: 0,
DiscountType: "Amount",
TaxPercent: d.taxPercent ?? 0,
}],
SellerInfo: {
SellerName: "<نام فروشنده>",
BizDomainId: "<BizDomainId فروشنده>", // الزامی
},
BuyerInfo: {
BuyerType: "Company",
BuyerName: d.buyer,
BuyerEconomicCode: d.buyerEconomicCode ?? "",
},
IssueDate: "2026-09-16T10:30:00.000Z",
};
await fetch(`https://app.didar.me/api/AccountingInvoice/Create?apikey=${API_KEY}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
return Response.json({ ok: true });
}
حسابفا یک حسابداری ابری با وبسرویس عمومی (همه متدها POST) است. اعتبارها (apiKey + loginToken یا userId+password) در بدنه درخواست ارسال میشوند و پاسخ همیشه در قالب{ Success, ErrorCode, ErrorMessage, Result } است:
| متد | Endpoint | توضیح |
|---|---|---|
| POST | /v1/accounting/GetSetting | تنظیمات حساب (از جمله TokenKey / LoginToken). |
| POST | /v1/invoice/save | ثبت/ویرایش فاکتور. |
| POST | /v1/invoice/get | دریافت فاکتور با id. |
| POST | /v1/Setting/Hook | هوک تغییرات (Hook) برای دریافت رویدادها. |
// وبهوک چرتکه ← ثبت فاکتور در حسابفا (Node.js / Next.js)
export async function POST(req) {
// تأیید امضا — کد بخش «وبهوک» بالا
const payload = await req.json();
if (payload.event !== "document.created") return Response.json({ ok: true });
const d = payload.data;
const res = await fetch("https://api.hesabfa.com/v1/invoice/save", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
apiKey: "<apiKey>", // تنظیمات حسابفا (بخش API)
loginToken: "<loginToken>", // یا userId + password
invoice: {
number: d.invoiceNumber ?? "",
date: "2026-09-16 10:30:00", // میلادی
dueDate: "2026-09-16 10:30:00",
contactCode: d.buyerContactCode, // کد شخص در حسابفا
contactTitle: d.buyer,
invoiceType: 1, // نوع فاکتور از جدول TypesTable (۰=فروش، ۱=خرید، …)
status: 2,
invoiceItems: [{
itemCode: d.itemCode,
description: d.title ?? "فاکتور چرتکه",
unit: "عدد",
quantity: Number(d.quantity ?? 1),
unitPrice: Number(d.finalAmount),
discount: 0,
tax: 0,
}],
},
}),
});
const data = await res.json();
if (!data.Success) console.error(data.ErrorCode, data.ErrorMessage);
return Response.json({ ok: true });
}
برخی نرمافزارهای دیگر نیز API / وبسرویس عمومی دارند (یا مستندات آن را باز کردهاند)؛ اما برای بیشتر آنها اتصال نیازمند هماهنگی با فروشنده نرمافزار است. بهروزترین وضعیت را همیشه از همین لینکها بررسی کنید:
| نرمافزار | نوع | مسیر اتصال |
|---|---|---|
| محک | حسابداری ابری | Swagger عمومی (محک) |
| تدبیر | ERP دسکتاپ | webservice.sppcco.com |
| پیوست، رافع، دشت، اوراش، پارمیس، پارسیان | متغیر | با فروشنده نرمافزار هماهنگ کنید. |
GET /api/v1/documents را با خروجی Excel/CSV ترکیب کنید و با ابزارهای داخلی همان نرمافزار وارد کنید.برای ساخت توکن به /app/settings بروید و در بخش «توکنهای API» نامی برای کلید تعریف کنید (مثلاً «سامانه مالی»). توکن به این شکل ساخته میشود:
chtk_abc123def456_4f3d…(48 کاراکتر hex)نکات امنیتی مهم:
در هر درخواست، توکن را در هدر استاندارد بفرستید:
Authorization: Bearer chtk_abc123def456_<48-char-secret>GET /api/v1/documentsلیست اسناد استخراجشده سازمان را با صفحهبندی و فیلتر برمیگرداند.
| پارامتر | نوع | پیشفرض | توضیح |
|---|---|---|---|
page | number | 1 | شماره صفحه نتایج (حداقل ۱). |
limit | number | 20 | تعداد نتایج در هر صفحه؛ بین ۱ تا ۱۰۰. |
search | string | — | جستوجوی متن در شماره فاکتور، نام فروشنده و خریدار. |
seller | string | — | فیلتر فروشنده با مطابقت دقیق. |
status | string | — | فیلتر بر اساس وضعیت سند (مثلاً draft / approved / rejected). |
fromJ | string (شمسی) | — | تاریخ شمسی شروع بازه (مثلاً 1404/01/01). |
toJ | string (شمسی) | — | تاریخ شمسی پایان بازه. |
curl -G "https://chortkey.ir/api/v1/documents" \
-H "Authorization: Bearer chtk_abc123def456_<48-char-secret>" \
--data-urlencode "page=1" \
--data-urlencode "limit=20" \
--data-urlencode "search=فاکتور شماره";
// Node.js یا Next.js (Route Handler / Server Action)
const TOKEN = "chtk_abc123def456_<48-char-secret>";
const res = await fetch("https://chortkey.ir/api/v1/documents?page=1&limit=20&search=فاکتور", {
headers: { Authorization: "Bearer " + TOKEN },
});
const result = await res.json();
console.log(result.data.items);
# Python 3 (با نصب: pip install requests)
import requests
TOKEN = "chtk_abc123def456_<48-char-secret>"
res = requests.get(
"https://chortkey.ir/api/v1/documents",
headers={"Authorization": "Bearer " + TOKEN},
params={"page": 1, "limit": 20},
)
data = res.json()
print(data["data"]["items"])
<?php
// PHP یا افزونه WordPress
$TOKEN = "chtk_abc123def456_<48-char-secret>";
$args = array(
"timeout" => 15,
"headers" => array("Authorization" => "Bearer " . $TOKEN),
);
$response = wp_remote_get(
"https://chortkey.ir/api/v1/documents?page=1&limit=20",
$args
);
$body = json_decode(wp_remote_retrieve_body($response), true);
print_r($body["data"]["items"]);
GET /api/v1/usageیکی از پارامترها ندارد و آمار کلی سازمان را برمیگرداند:
curl "https://chortkey.ir/api/v1/usage" \
-H "Authorization: Bearer chtk_abc123def456_<48-char-secret>";
{
"success": true,
"data": {
"totalDocuments": 42,
"currentPeriodDocuments": 7,
"byStatus": [
{ "status": "draft", "count": 20 },
{ "status": "approved", "count": 15 },
{ "status": "rejected", "count": 7 }
],
"byThreeWayMatch": [
{ "threeWayMatch": "تأیید شد", "count": 30 },
{ "threeWayMatch": "-", "count": 12 }
]
},
"meta": {
"organizationId": "org-xxxx",
"apiVersion": "v1"
}
}currentPeriodDocuments تعداد اسناد دوره جاری (ماه جاری میلادی) را نشان میدهد و برای کنترل سهمیه و بودجه دوره کاربرد دارد.
وبهوک یک «قلاب» است: به محض رخدادن رویداد، چرتکه یک درخواست HTTP را با محتوای JSON به آدرس شما میفرستد و دیگر نیازی به چککردن دورهای نیست. این روش برای ثبت خودکار فاکتور در نرمافزار حسابداری، ارسال به ربات تلگرام، داشبورد زنده و CRM مناسب است.
document.createdسند جدید ذخیره/پردازش شد (ذخیره فاکتور)نمونه بدنِ درخواست ارسالی به آدرس شما:
{
"event": "document.created",
"data": {
"id": "uuid",
"invoiceNumber": "1234",
"seller": "شرکت الف",
"buyer": "شرکت ب",
"issueDateJ": "1404/01/15",
"finalAmount": 12500000,
"threeWayMatch": "-"
},
"sentAt": "2026-09-16T10:30:00.000Z"
}هر درخواست وبهوک با هدر امضا میشود:
X-Chortke-Signature: t=1770000000,v1=<64-char-hmac-sha256>t زمان ارسال (ثانیه Unix) و v1 مقدار HMAC-SHA256 بدنه درخواست با «کلید محرمانه» وبهوک است. کلید محرمانه هنگام ساخت وبهوک فقط یکبار نمایش داده میشود. همیشه پیش از پردازش، امضا را بررسی کنید؛ این کار از ارسالهای جعلی و درخواستهای بازپخش جلوگیری میکند.
// تأیید امضای وبهوک در Node.js / Next.js
import { createHmac, timingSafeEqual } from "crypto";
export function verifyWebhook(rawBody, signatureHeader, secret) {
const parts = signatureHeader.split(",");
const timestamp = String(parts.find((p) => p.startsWith("t="))?.slice(2) ?? "");
const signature = parts.find((p) => p.startsWith("v1="))?.slice(3) ?? "";
// ضد بازپخش: بازه زمانی ۵ دقیقه
const now = Math.floor(Date.now() / 1000);
if (now - Number(timestamp) > 300) return false;
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(signature, "hex");
const b = Buffer.from(expected, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
// در Route Handler:
export async function POST(req) {
const rawBody = await req.text();
const header = req.headers.get("x-chortke-signature") ?? "";
const ok = verifyWebhook(rawBody, header, "YOUR_WEBHOOK_SECRET");
if (!ok) return new Response("invalid signature", { status: 401 });
const payload = JSON.parse(rawBody);
// حالا payload.event === "document.created"
return new Response("ok", { status: 200 });
}
# دریافتکننده وبهوک با FastAPI / Flask
import hashlib
import hmac
import time
from fastapi import FastAPI, Request, Header
app = FastAPI()
WEBHOOK_SECRET = "YOUR_WEBHOOK_SECRET"
@app.post("/webhook")
async def webhook(request: Request, x_chortke_signature: str = Header("")):
raw_body = (await request.body()).decode()
parts = dict(p.split("=", 1) for p in x_chortke_signature.split(","))
ts = int(parts.get("t", "0"))
signature = parts.get("v1", "")
if int(time.time()) - ts > 300:
return {"error": "expired"}
expected = hmac.new(
WEBHOOK_SECRET.encode(),
raw_body.encode(),
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(signature, expected):
return {"error": "invalid"}, 401
payload = await request.json()
# payload["event"] == "document.created"
return {"ok": True}
<?php
// دریافتکننده وبهوک در PHP / WordPress
define("WEBHOOK_SECRET", "YOUR_WEBHOOK_SECRET");
$rawBody = file_get_contents("php://input");
$signatureHeader = $_SERVER["HTTP_X_CHORTKE_SIGNATURE"] ?? "";
$secret = constant("WEBHOOK_SECRET");
$parts = [];
foreach (explode(",", $signatureHeader) as $item) {
[$k, $v] = explode("=", $item, 2);
$parts[$k] = $v;
}
if (time() - (int)($parts["t"] ?? 0) > 300) {
http_response_code(401);
exit;
}
$expected = hash_hmac("sha256", $rawBody, $secret);
if (!hash_equals($expected, $parts["v1"] ?? "")) {
http_response_code(401);
exit;
}
$payload = json_decode($rawBody, true);
// $payload["event"] === "document.created"
http_response_code(200);
echo "ok";
X-Chortke-Signature را با timingSafeEqual/hash_equals بررسی و بازه زمانی t را محدود کنید (مثال: ۵ دقیقه).| مشکل | علت و راهحل |
|---|---|
| پاسخ HTTP 401 «توکن نامعتبر یا لغو شده» | توکن اشتباه، دارای کاراکتر فضای خالی، یا قبلاً لغو شده است. توکن جدید بسازید و دقیقاً در هدر Authorization قرار دهید. |
| وبهوک ارسال میشود اما دیتا ثبت نمیشود | امضای هدر را بررسی کنید؛ متداولترین علت، خطا در کپی کلید محرمانه یا اشتباه در HMAC است. |
| وبهوک پاسخ نمیگیرد (Timeout) | سرور شما باید زیر ۱۰ ثانیه پاسخ دهد؛ وبهوک را موقتاً غیرفعال کنید و سرور را بررسی کنید. |
| تاریخ ازJ / تاJ کار نمیکند | این پارامترها به شمسی (Jalali) هستند مثل 1404/01/01؛ نه MILADI. |
| چرا اصل توکن دوباره نمایش داده نمیشود؟ | برای امنیت، فقط در لحظه ساخت نمایش داده میشود. اگر گم شد، توکن قبلی را لغو و توکن جدید بسازید. |
آماده اتصال هستید؟ ابتدا در تنظیمات توسعهدهنده توکن یا وبهوک بسازید و در صورت نیاز به کمک با مرکز پشتیبانی در تماس باشید.