بالنج اطلس رزرو جلسه

راهنمای API رویدادهای مالی

این راهنما برای برنامه‌نویسانی است که می‌خواهند رویدادهای مالی کسب‌وکار را از سیستم خودشان، به‌طور خودکار، برای بالنج بفرستند.

به‌زودی. این API و اتصال MCP آن در حال آماده شدن است. اگر می‌خواهید سیستمتان را زودتر وصل کنید، با ما تماس بگیرید.

رزرو جلسه

با کمک هوش مصنوعی کد می‌نویسید؟ دستیارتان را به سرور MCP بالنج وصل کنید. از آن به بعد، دستیار این راهنما و شرح فنی کامل را خودش می‌خواند و می‌تواند کد فرستادن رویدادها را برایتان بنویسد.

اتصال MCP
claude mcp add --transport http balenj https://atlas.balenj.com/mcp

همه‌ی داده‌ها از این API نمی‌آید. وقتی با بالنج کار می‌کنید، علاوه بر رویدادهایی که خودتان می‌فرستید، با اجازه‌ی شما از منابع داده‌ی دیگرتان هم اطلاعات جمع می‌کنیم: گزارش درگاه‌های پرداخت، صورت‌حساب بانک و منابع دیگر. خیلی وقت‌ها همین کافی است و اصلاً لازم نیست از سمت شما رویدادی فرستاده شود.

این API برای اطلاعاتی است که فقط در سیستم خود شما هست، مثل ریز سفارش‌ها، کالاها و برچسب‌ها.

شروع سریع

  1. کلید API بگیرید. دو کلید از راهی امن به شما می‌دهیم: یکی آزمایشی و یکی اصلی. رویدادهایی که با کلید آزمایشی بفرستید، در هیچ گزارشی حساب نمی‌شوند.
  2. شناسه‌ی حساب‌ها را بگیرید. حساب بانکی، درگاه پرداخت و صندوقتان را ما در پنل می‌سازیم و شناسه‌ی هر کدام را به شما می‌دهیم.
  3. اولین رویداد را بفرستید. نمونه‌ی یک فروش ساده، با curl و پایتون، کنار همین متن آمده است.
  4. پاسخ را بررسی کنید. کد 200 یعنی رویداد ثبت شده است. اگر ایرادی داشته باشد، در issues می‌آید؛ ما هم آن را در پنل خودمان می‌بینیم و بررسی می‌کنیم.

هشدار: کلید API را در کد، لاگ یا پیام‌ها ننویسید و آن را در متغیر محیطی سرور نگه دارید. اگر کلید جایی فاش شد، به ما خبر بدهید تا کلید تازه‌ای صادر کنیم.

اولین رویداد
curl https://atlas.balenj.com/api/v1/events \
  -H "Authorization: Bearer $BALENJ_KEY" \
  -H "Idempotency-Key: order-10470-1" \
  -H "Content-Type: application/json" \
  -d @- <<'JSON'
{
  "id": "order-10470",
  "type": "sale",
  "state": "final",
  "occurred_at": "1405/08/10 14:30",
  "charges": [
    {
      "kind": "price",
      "amount": 24000000
    }
  ],
  "payments": [
    {
      "direction": "in",
      "amount": 24000000,
      "account": "acc-zarinpal"
    }
  ]
}
JSON
200 · پاسخ
{
  "received": true,
  "id": "order-10470",
  "issues": []
}

مفاهیم پایه

رویداد

رویداد هر اتفاق مالی است: فروش، خرید، هزینه، دریافت قسط یا لغو. هر رویداد را همان زمانی که رخ می‌دهد، با یک درخواست بفرستید. اطلاعات مشتری، کالا و برچسب‌ها داخل خود رویداد می‌آیند و درخواست جداگانه‌ای لازم ندارند.

شناسه

شناسه همان شماره‌ای است که سیستم شما به رویداد داده، مثل شماره‌ی سفارش. نوعش رشته است، پس هم order-12345 درست است و هم INV/1405/88. لازم نیست از ما شماره‌ای بگیرید.

شناسه‌ی هر رویداد باید با شناسه‌ی رویدادهای لغونشده‌ی دیگر فرق داشته باشد. اگر رویدادی با همان شناسه و همان محتوا دوباره برسد، یک بار حساب می‌شود؛ اگر محتوایش فرق کند و edit نداشته باشد، خطای 409 با کد duplicate_id برمی‌گردد و رویداد دوم اثری ندارد؛ برای اصلاح، همان رویداد را با "edit": true بفرستید. مشتری‌ها و کالاها هم با شناسه و نامشان در سیستم شما می‌آیند و بار اولی که برسند، ثبت می‌شوند.

حالت

هر رویداد یکی از سه حالت را دارد:

  • draft (پیش‌نویس): هنوز معلوم نیست انجام شود و در هیچ گزارشی حساب نمی‌شود.
  • recorded (ثبت‌شده): انجام شده و در گزارش‌ها حساب می‌شود.
  • final (قطعی): فقط برای فروشی که کالایش تحویل یا خدمتش کامل ارائه شده است. فروش‌های قطعی را برای ثبت در سامانه‌ی مودیان بررسی می‌کنیم.

رویداد ثبت‌شده ویرایش نمی‌شود. هر اتفاقی که بعداً بیفتد، رویداد تازه‌ای است که با of به رویداد قبلی اشاره می‌کند.

تاریخ و مبلغ

تاریخ را شمسی، مثل 1405/08/10 14:30، یا میلادی، مثل 2026-11-01T14:30:00+03:30، بفرستید؛ قالب دقیق هر دو در بخش «فرستادن رویداد» آمده است. رقم‌ها باید لاتین باشند. اگر منطقه‌ی زمانی را ننویسید، ساعت به وقت تهران حساب می‌شود.

مبلغ‌ها به ریال و عدد صحیح‌اند، بدون جداکننده‌ی هزارگان: 24000000.

ایرادها

هر رویدادی که برسد ثبت می‌شود، حتی اگر ایراد داشته باشد. ایرادها در پنل ما نمایش داده می‌شوند؛ آن‌ها را بررسی می‌کنیم و در صورت نیاز به شما خبر می‌دهیم.

نام‌های یکسان

نام‌ها را یکدست می‌کنیم؛ مثلاً «ي» و «ك» عربی را به فارسی برمی‌گردانیم و فاصله‌ها را مرتب می‌کنیم. اگر دو شناسه پس از این کار نام یکسانی داشته باشند، یک مشتری یا یک کالا حساب می‌شوند، حتی اگر شناسه‌هایشان فرق کند. این ادغام در پنل ما ثبت می‌شود و در پاسخ هم با کد merged_by_name به شما خبر داده می‌شود.

هشدار: اگر ممکن است مشتری‌های متفاوتی با نام یکسان داشته باشید، چیزی را که آن‌ها را از هم جدا می‌کند به نامشان اضافه کنید، مثل شهر یا چهار رقم آخر موبایل: «مریم کاظمی (تهران)». برای کالاهای هم‌نام هم، مثلاً اندازه یا رنگ را در نام بیاورید. وگرنه کیف پول، مانده‌ی حساب یا موجودی انبارشان یکی می‌شود.

POST/events

فرستادن رویداد

یک رویداد را ثبت می‌کند. برای فرستادن چند رویداد با هم، حداکثر ۵۰۰ رویداد، از POST /events/batch استفاده کنید و رویدادها را در آرایه‌ای به نام events بگذارید.

idرشتهالزامی

شناسه‌ای که سیستم شما به رویداد داده است.

typeیکی از مقادیرالزامی

نوع رویداد.

نوع‌ها
saleفروش، نقد یا نسیه یا قسطی
prepaymentپولی که پیش از فروش رسیده، مثل شارژ کیف پول
purchase_goodsخرید کالا برای انبار
purchase_equipmentخرید تجهیزات
running_expenseهزینه‌ی جاری، حقوق هم جزو آن است
payment_to_stateپرداخت به دولت، مثل بیمه‌ی کارکنان
owners_money_or_loanپول صاحب کسب‌وکار یا شریک‌ها، یا وام
own_transferجابه‌جایی پول بین حساب‌های خودتان
settlementتسویه با طرف حساب، قسط هم جزو آن است
returnبرگشت و لغو
completionقطعی شدن فروشی که قبلاً ثبت شده؛ فقط با of
occurred_atتاریخالزامی

زمان رخ دادن رویداد، در یکی از این دو قالب:

تقویمقالب و نمونه
شمسیYYYY/MM/DD یا YYYY/MM/DD HH:MM، مثل 1405/08/10 و 1405/08/10 14:30
میلادیYYYY-MM-DD یا YYYY-MM-DDTHH:MM:SS+03:30، مثل 2026-11-01 و 2026-11-01T14:30:00+03:30

تقویم از روی قالب تشخیص داده می‌شود: شمسی با خط مورب (/) و سال ۱۳ یا ۱۴، میلادی با خط تیره (-) و سال ۲۰. تاریخی که در هیچ‌کدام از این دو قالب نباشد، ثبت می‌شود اما ایراد unreadable_date می‌گیرد.

statedraft · recorded · final

حالت رویداد. اگر نفرستید، recorded در نظر گرفته می‌شود.

partyآبجکت

طرف حساب اصلی؛ مثلاً در فروش، خریدار.

فیلدهای طرف حساب
idرشتهالزامی

شناسه‌ی طرف حساب در سیستم شما.

nameرشته

نام.

phoneرشته

شماره‌ی موبایل.

national_idرشته

اختیاری: کد ملی برای شخص حقیقی، یا شناسه‌ی ملی برای شرکت. ۱۰ یا ۱۱ رقم لاتین، بدون خط تیره.

roleیکی از مقادیر

نقش طرف حساب، یکی از این‌ها:

مقدارمعنی
buyerخریدار؛ کسی که از شما خرید می‌کند
supplierتأمین‌کننده؛ کسی که کالا یا خدمت به شما می‌فروشد
shopفروشگاهی که کالایش را از طریق شما می‌فروشید
reseller_or_influencerاینفلوئنسر یا بازاریابی که برایتان فروش می‌آورد و سهم می‌گیرد
marketplaceبازارگاه آنلاین، مثل دیجی‌کالا یا باسلام
carrierباربری یا شرکت حمل بین‌شهری
line_operatorراننده یا متصدی خط حمل
courierپیک یا پست
gatewayدرگاه پرداخت یا شرکت پرداخت قسطی، مثل زرین‌پال یا اسنپ‌پی
stateدولت، مثل اداره‌ی مالیات یا سازمان تأمین اجتماعی
partner_or_lenderشریک یا وام‌دهنده
ofرشته

شناسه‌ی رویداد قبلی، برای رویدادی که به آن مربوط است؛ مثلاً قسطِ یک فروش، هزینه‌ی یک سفارش، قطعی شدن یا لغو.

descriptionرشته

توضیح شما درباره‌ی رویداد. در فهرست رویدادها همین متن نمایش داده می‌شود.

labelsآبجکت کلید و مقدار

برچسب‌ها دسته‌بندی‌هایی‌اند که می‌خواهید گزارش‌هایتان را بر اساس آن‌ها جدا ببینید. هر کسب‌وکار برچسب‌های خودش را دارد و لازم نیست از قبل جایی تعریفشان کنید: اسم برچسب و مقدارش را همان‌طور که در سیستم خودتان دارید، داخل رویداد بفرستید.

  • یک کافه با دو بخش بازی و غذا می‌تواند برچسب «بخش» را با مقدار «بازی» یا «غذا» بفرستد. بعد سود هر بخش را جدا می‌بیند.
  • یک فروشگاه آنلاین می‌تواند برچسب «فروشگاه» را بفرستد تا فروش هر فروشگاه جدا دیده شود.
  • یک رویداد می‌تواند چند برچسب داشته باشد. هر قلم هم می‌تواند برچسب خودش را داشته باشد، اگر با کل رویداد فرق دارد.
  • رویدادی که با of به رویداد دیگری وصل است، مثل قسط یا کرایه‌ی بعدی، برچسب‌های همان رویداد را خودبه‌خود می‌گیرد؛ لازم نیست دوباره بفرستید.
  • اگر برچسبی را برای رویدادی نفرستید، خالی می‌ماند و تا پر نشود، آن ماه بسته نمی‌شود. اگر بعداً معلوم شد، رویداد را اصلاح کنید؛ اگر نشد، ما در پنل پر می‌کنیم.
"labels": {
  "بخش": "غذا",
  "فروشگاه": "شعبه‌ی مرکزی"
}
linesآرایه

قلم‌های فاکتور؛ بخش بعد را ببینید.

chargesآرایه

ریز قیمت؛ بخش بعد را ببینید.

paymentsآرایه

پرداخت‌ها؛ بخش بعد را ببینید.

instalmentsآرایه

فقط در فروش قسطی: جدول اقساط، هر قسط با شماره (number)، سررسید (due_at) و مبلغ (amount). سناریوی «فروش قسطی» را ببینید.

cancel، editبولین

cancel برای لغو و edit برای اصلاح؛ سناریوهای «لغو» و «اصلاح اشتباه» را ببینید.

sale_subjectیکی از مقادیر

فقط در فروش، نوع چیزی که فروخته شده: goods کالا، subscription اشتراک، freight_service حمل، consultation مشاوره.

metadataآبجکت کلید و مقدار

هر اطلاعات دیگری که می‌خواهید همراه رویداد بماند؛ حداکثر ۵۰ کلید با مقدار رشته.

رویدادی که یکی از فیلدهای الزامی را نداشته باشد هم ثبت می‌شود، اما ایراد می‌گیرد.

POST /events · نمونه‌ی کامل
{
  "id": "order-10482",
  "type": "sale",
  "state": "final",
  "occurred_at": "1405/08/10 14:30",
  "party": {
    "id": "cust-889",
    "name": "سارا احمدی",
    "phone": "09120000000"
  },
  "description": "کرم ضدآفتاب و ماسک، سفارش اینستاگرام",
  "labels": {
    "فروشگاه": "فروشگاه نمونه"
  },
  "lines": [
    {
      "item": {
        "id": "SUN-50",
        "name": "کرم ضدآفتاب ۵۰",
        "stocked": true
      },
      "qty": 1,
      "unit_price": 18000000
    },
    {
      "item": {
        "id": "MASK-02",
        "name": "ماسک صورت",
        "stocked": true
      },
      "qty": 2,
      "unit_price": 3000000
    }
  ],
  "charges": [
    {
      "kind": "price",
      "amount": 24000000
    },
    {
      "kind": "surcharge",
      "reason": "freight",
      "direction": "in",
      "amount": 600000
    }
  ],
  "payments": [
    {
      "direction": "in",
      "amount": 24600000,
      "account": "acc-zarinpal"
    }
  ]
}
200 · پاسخ
{
  "received": true,
  "id": "order-10482",
  "issues": []
}

قیمت (۲۴ میلیون ریال) جمع قلم‌هاست. خریدار ۲۴٫۶ میلیون ریال پرداخته است: قیمت به‌اضافه‌ی ۶۰۰ هزار ریال هزینه‌ی ارسال.

قلم‌ها، ریز قیمت و هزینه‌ها، پرداخت‌ها

قلم‌ها lines

قلم‌های فاکتور: در فروش، کالا یا خدمتی که خریدار از شما خریده است؛ در خرید، چیزی که شما خریده‌اید.

فیلدمعنی
item.idشناسه‌ی کالا در سیستم شما
item.nameنام کالا
item.unitواحد، مثل «عدد»
item.stockedدر انبار خودتان نگه داشته می‌شود
qtyتعداد
unit_priceقیمت واحد، به ریال
labelsبرچسب همین قلم، اگر با رویداد فرق دارد
restockدر برگشت: کالا به انبار برگشته

نام و مشخصات کالا از آخرین رویدادی که دریافت شده برداشته می‌شود.

یک قلم
{
  "item": {
    "id": "SUN-50",
    "name": "کرم ضدآفتاب ۵۰",
    "unit": "عدد",
    "stocked": true
  },
  "qty": 2,
  "unit_price": 18000000
}

ریز قیمت و هزینه‌ها charges

هر مبلغی که در این رویداد قرار گذاشته شده است، چه پولش جابه‌جا شده باشد چه نه. دو جور مبلغ این‌جا می‌آید:

  • آنچه خریدار به شما بدهکار است: قیمت، منهای تخفیف، به‌اضافه‌ی هزینه‌ی ارسالی که از خریدار گرفته‌اید.
  • آنچه شما برای همین سفارش به دیگران بدهکارید: سهم تأمین‌کننده، کمیسیون اینفلوئنسر، کرایه‌ی پیک یا بسته‌بندی‌ای که خودتان پرداخته‌اید. این‌ها هزینه‌های همین سفارش‌اند و سودش را درست نشان می‌دهند.

پولی که واقعاً جابه‌جا شده، چه از خریدار گرفته باشید و چه به تأمین‌کننده داده باشید، در بخش بعد، «پرداخت‌ها»، می‌آید.

نوعمعنی
priceقیمت فروش؛ جمع قلم‌ها
discountتخفیف
surchargeهزینه‌ی جانبی، مثل ارسال
purchase_priceقیمت خرید، سهم تأمین‌کننده یا کرایه
commissionکمیسیون
damageخسارت
taxچه مقدار از قیمت مالیات است؛ اگر مبلغش را دارید
reversalکم شدن مبلغ توافق‌شده
payment_feeکارمزد درگاه؛ اختیاری
  • هزینه‌ی جانبی دو فیلد دیگر هم دارد: reason (مثلاً freight برای ارسال) و direction؛ اگر هزینه را از خریدار گرفته‌اید in و اگر خودتان پرداخته‌اید out.
  • هر ردیف می‌تواند طرف حساب خودش (party) و شماره‌ی قلم مربوط (line، از صفر) را هم داشته باشد.
  • کارمزد درگاه را از گزارش خود درگاه می‌خوانیم و بهای تمام‌شده‌ی کالا را از روی خریدها حساب می‌کنیم؛ لازم نیست این دو را بفرستید.
  • قیمت، تخفیف و برگشت همیشه با مالیات‌اند؛ ردیف tax فقط می‌گوید چه مقدار از آن‌ها مالیات است. اگر مشمول ارزش افزوده هستید و این ردیف را نفرستید، خودمان آن را از داخل قیمت حساب و اضافه می‌کنیم، با برچسب «محاسبه‌ی بالنج».
ریز قیمت و هزینه‌های یک فروش
[
  {
    "kind": "price",
    "amount": 24000000
  },
  {
    "kind": "discount",
    "amount": 1000000
  },
  {
    "kind": "surcharge",
    "reason": "freight",
    "direction": "in",
    "amount": 600000
  },
  {
    "kind": "purchase_price",
    "amount": 16000000,
    "party": {
      "id": "sup-defacto",
      "name": "دیفکتو",
      "role": "supplier"
    }
  },
  {
    "kind": "commission",
    "amount": 2000000,
    "party": {
      "id": "inf-31",
      "name": "صفحه‌ی زیبایی نمونه",
      "role": "reseller_or_influencer"
    }
  },
  {
    "kind": "surcharge",
    "reason": "freight",
    "direction": "out",
    "amount": 500000,
    "party": {
      "id": "post",
      "name": "پست",
      "role": "courier"
    }
  }
]

این سفارش کالایی است که در انبار شما نیست و تأمین‌کننده مستقیم برای خریدار می‌فرستد؛ برای همین سهمش این‌جا می‌آید. برای کالای انبار خودتان این ردیف را نفرستید، چون بهای تمام‌شده‌اش را از روی خریدها حساب می‌کنیم. خریدار ۲۳٫۶ میلیون ریال بدهکار است: ۲۴ میلیون قیمت، منهای ۱ میلیون تخفیف، به‌اضافه‌ی ۶۰۰ هزار ارسال. هزینه‌های همین سفارش ۱۸٫۵ میلیون است: ۱۶ میلیون سهم تأمین‌کننده، ۲ میلیون کمیسیون و ۵۰۰ هزار کرایه‌ی پست. پس سود این سفارش ۵٫۱ میلیون ریال است.

پرداخت‌ها payments

پولی که واقعاً جابه‌جا شده است، به همان مبلغی که به حساب واریز یا از آن برداشت شده.

فیلدمعنی
direction *in یعنی پول به حساب شما آمده؛ out یعنی از حساب شما رفته
amount *مبلغ، به ریال
accountشناسه‌ای که ما به حساب داده‌ایم
walletاگر از کیف پول خریدار پرداخت شده؛ به‌جای account
atاگر زمان پرداخت با زمان رویداد فرق دارد
instalmentشماره‌ی قسط
in_trustپول امانی که مال شما نیست

ستاره یعنی این فیلد الزامی است.

دو پرداخت
[
  {
    "direction": "in",
    "amount": 20000000,
    "account": "acc-zarinpal"
  },
  {
    "direction": "in",
    "amount": 3600000,
    "wallet": true
  }
]

درخواست‌های دیگر

POST/events/batch

حداکثر ۵۰۰ رویداد، در آرایه‌ای به نام events. پاسخ آرایه‌ای به نام results است که برای هر رویداد، به همان ترتیب، یک پاسخ دارد. اگر بیش از ۵۰۰ رویداد دارید، آن‌ها را در چند درخواست بفرستید.

GET/events?since=…

رویدادهایی که از زمان مشخص‌شده به دست ما رسیده‌اند، به ترتیب زمان دریافت و در صفحه‌های حداکثر ۵۰۰تایی. برای صفحه‌ی بعد، مقدار next را در پارامتر cursor بفرستید؛ در صفحه‌ی آخر، next برابر null است.

GET/events/{id}

همه‌ی رویدادهایی که این شناسه را دارند، همراه با ایرادهایشان. اگر رویدادی اصلاح شده باشد، هم نسخه‌ی لغوشده و هم نسخه‌ی درست برگردانده می‌شود.

DELETE/events/{id}

فقط پیش‌نویس را حذف می‌کند. اگر رویداد ثبت‌شده باشد، کاری انجام نمی‌دهد و ایراد not_a_draft برمی‌گرداند.

پاسخ‌ها و خطاها

کد 200 یعنی رویداد ثبت شده است، حتی اگر issues خالی نباشد. ایرادها در پنل ما هم نمایش داده می‌شوند و لازم نیست کاری بکنید، مگر این‌که بخواهید علتشان را در سیستم خودتان برطرف کنید.

کدمعنی و کاری که باید بکنید
200رویداد ثبت شد.
409رویداد لغونشده‌ی دیگری همین شناسه را دارد و این یکی edit ندارد. اثری ندارد؛ اگر قصد اصلاح داشتید، با "edit": true بفرستید.
401کلید API ارسال نشده یا نادرست است. چیزی ثبت نشده است.
413حجم درخواست بیش از ۱ مگابایت است. رویدادها را در چند درخواست بفرستید.
429بیش از ۲۰ درخواست در ثانیه فرستاده‌اید. به تعداد ثانیه‌هایی که در هدر Retry-After آمده صبر کنید و دوباره بفرستید.
5xxخطا از سمت ماست. درخواست را با همان Idempotency-Key دوباره بفرستید.

هدر Idempotency-Key اختیاری است، اما توصیه می‌کنیم آن را بفرستید. اگر درخواستی با همین مقدار تا ۲۴ ساعت بعد دوباره برسد، چیزی دو بار ثبت نمی‌شود و همان پاسخ قبلی برمی‌گردد.

200 · پاسخ با ایراد
{
  "received": true,
  "id": "order-10482",
  "issues": [
    {
      "code": "lines_do_not_match_price",
      "field": "charges[0]",
      "message": "جمع قلم‌ها با قیمت یکی نیست."
    }
  ]
}

سناریوها

برای هر سناریو، توضیح و درخواست کنار هم آمده‌اند. شناسه‌ها، نام‌ها و مبلغ‌ها ساختگی‌اند.

فروش با برچسب بخش

فروش دو بستنی در بخش غذا که با کارتخوان همان بخش پرداخت شده است. فروش همان لحظه کامل شده، پس حالتش قطعی است.

درخواست
POST /events
{
  "id": "pos-5531",
  "type": "sale",
  "state": "final",
  "occurred_at": "1405/08/10 17:20",
  "labels": {
    "بخش": "غذا"
  },
  "lines": [
    {
      "item": {
        "id": "ice-cream",
        "name": "بستنی"
      },
      "qty": 2,
      "unit_price": 1500000
    }
  ],
  "charges": [
    {
      "kind": "price",
      "amount": 3000000
    }
  ],
  "payments": [
    {
      "direction": "in",
      "amount": 3000000,
      "account": "acc-pos-food"
    }
  ]
}

شارژ کیف پول و خرید با آن

کیف پول هر خریدار، پیش‌دریافتِ همان خریدار است و با شناسه‌ی او (party.id) شناخته می‌شود. دو رویداد:

  1. شارژ: نوع prepayment یعنی این پول به کیف پول این خریدار رفت. پرداختش هم پول واقعی است که به حساب درگاه شما آمده.
  2. خرید با کیف پول: پرداخت با "wallet": true یعنی پول از کیف پول همان خریدار برداشته شد. پولی جابه‌جا نمی‌شود و فقط از مانده‌ی کیف پولش کم می‌شود.

چون شناسه‌ی خریدار در هر دو رویداد یکی است (user-5120)، مانده‌ی کیف پولش همیشه معلوم است: اینجا ۱۰ میلیون منهای ۵ میلیون.

درخواست
POST /events
{
  "id": "topup-3307",
  "type": "prepayment",
  "occurred_at": "1405/08/10",
  "description": "شارژ کیف پول",
  "party": {
    "id": "user-5120",
    "name": "مریم کاظمی"
  },
  "payments": [
    {
      "direction": "in",
      "amount": 10000000,
      "account": "acc-zarinpal"
    }
  ]
}

POST /events
{
  "id": "order-20931",
  "type": "sale",
  "state": "final",
  "occurred_at": "1405/08/13",
  "description": "خرید با کیف پول",
  "party": {
    "id": "user-5120",
    "name": "مریم کاظمی"
  },
  "charges": [
    {
      "kind": "price",
      "amount": 5000000
    }
  ],
  "payments": [
    {
      "direction": "in",
      "amount": 5000000,
      "wallet": true
    }
  ]
}

سفارش پرداخت‌نشده: پیش‌نویس، بعد ثبت

سفارشی در سایت شما ثبت شده، اما هنوز پرداخت نشده و معلوم نیست نهایی شود. آن را به‌صورت پیش‌نویس بفرستید تا در گزارش‌ها حساب نشود. وقتی پول رسید، همان شناسه را با حالت recorded و پرداختش دوباره بفرستید. اگر سفارش انجام نشد، پیش‌نویس را با DELETE /events/order-30115 حذف کنید.

تا پیش‌نویسی باز باشد، ماه آن بسته نمی‌شود؛ پس پیش‌نویس‌ها را تأیید یا حذف کنید.

درخواست
POST /events
{
  "id": "order-30115",
  "type": "sale",
  "state": "draft",
  "occurred_at": "1405/08/15",
  "party": {
    "id": "cust-77",
    "name": "رضا نوری"
  },
  "charges": [
    {
      "kind": "price",
      "amount": 8000000
    }
  ]
}

POST /events
{
  "id": "order-30115",
  "type": "sale",
  "state": "recorded",
  "occurred_at": "1405/08/15",
  "party": {
    "id": "cust-77",
    "name": "رضا نوری"
  },
  "charges": [
    {
      "kind": "price",
      "amount": 8000000
    }
  ],
  "payments": [
    {
      "direction": "in",
      "amount": 8000000,
      "account": "acc-zarinpal"
    }
  ]
}

فروشی که بعداً قطعی می‌شود

اشتراک سه‌ماهه امروز فروخته و پولش دریافت می‌شود، اما خدمتش تا سه ماه بعد ارائه می‌شود. آن را با حالت recorded بفرستید و وقتی خدمت تمام شد، یک رویداد completion بفرستید. فروش‌های قطعی را برای ثبت در سامانه‌ی مودیان بررسی می‌کنیم و وضعیت ثبتشان را در داشبورد می‌بینید.

درخواست
POST /events
{
  "id": "sub-118",
  "type": "sale",
  "state": "recorded",
  "occurred_at": "1405/08/01",
  "sale_subject": "subscription",
  "party": {
    "id": "parent-44",
    "name": "خانواده‌ی رحیمی"
  },
  "charges": [
    {
      "kind": "price",
      "amount": 45000000
    }
  ],
  "payments": [
    {
      "direction": "in",
      "amount": 45000000,
      "account": "acc-bank"
    }
  ]
}

POST /events
{
  "id": "sub-118-done",
  "type": "completion",
  "of": "sub-118",
  "occurred_at": "1405/10/30"
}

فروش قسطی و رسیدن قسط

خود فروش در حالت recorded فرستاده می‌شود، با قیمت کل و جدول اقساطش در instalments: شماره، سررسید و مبلغ هر قسط. پیش‌پرداخت، اگر هست، قسط اول است. وقتی کار تمام شد، فروش با completion قطعی می‌شود.

هر قسطی که می‌رسد، یک رویداد تسویه‌ی جداگانه است که با of به فروش اشاره می‌کند و شماره‌ی قسط را در instalment می‌گوید. این‌طوری معلوم است کدام قسط‌ها رسیده و چقدر مانده است.

اگر جدول اقساط عوض شد، فروش را مثل هر اشتباه دیگری اصلاح کنید: همان فروش را با "edit": true و جدول تازه دوباره بفرستید.

درخواست
POST /events
{
  "id": "case-450",
  "type": "sale",
  "state": "recorded",
  "occurred_at": "1405/06/01",
  "party": {
    "id": "cust-450",
    "name": "علی مرادی"
  },
  "charges": [
    {
      "kind": "price",
      "amount": 150000000
    }
  ],
  "instalments": [
    {
      "number": 1,
      "due_at": "1405/06/01",
      "amount": 50000000
    },
    {
      "number": 2,
      "due_at": "1405/07/01",
      "amount": 50000000
    },
    {
      "number": 3,
      "due_at": "1405/08/01",
      "amount": 50000000
    }
  ]
}

POST /events
{
  "id": "case-450-inst-3",
  "type": "settlement",
  "of": "case-450",
  "occurred_at": "1405/08/20",
  "party": {
    "id": "cust-450",
    "name": "علی مرادی"
  },
  "payments": [
    {
      "direction": "in",
      "amount": 50000000,
      "account": "acc-bank",
      "instalment": 3
    }
  ]
}

هزینه‌ای که بعداً معلوم می‌شود

کرایه‌ی باربر سه روز بعد از سفارش معلوم می‌شود. یک رویداد هزینه بفرستید که با of به سفارش اشاره کند تا سود همان سفارش درست محاسبه شود.

درخواست
POST /events
{
  "id": "order-7812-fare",
  "type": "running_expense",
  "of": "order-7812",
  "occurred_at": "1405/08/18",
  "party": {
    "id": "driver-221",
    "name": "حمید صالحی",
    "role": "carrier"
  },
  "charges": [
    {
      "kind": "purchase_price",
      "amount": 4500000
    }
  ]
}

لغو و پس دادن پول

یک رویداد return با "cancel": true و of بفرستید. پولی که برگردانده شده، پرداختی با جهت out است. اگر فروش از کیف پول پرداخت شده بود، پرداخت برگشتی هم "wallet": true دارد. رویدادی را هم که در سیستم خودتان حذف می‌کنید، به همین شکل لغو کنید. فروش قطعی‌شده لغو نمی‌شود؛ اگر پولش را پس دادید، یک return بدون cancel بفرستید، مثل سناریوی «مرجوعی بخشی از یک فروش».

درخواست
POST /events
{
  "id": "order-10501-cancel",
  "type": "return",
  "of": "order-10501",
  "cancel": true,
  "occurred_at": "1405/08/11",
  "payments": [
    {
      "direction": "out",
      "amount": 12000000,
      "account": "acc-zarinpal"
    }
  ]
}

لغو فروشی که قسطش رسیده

فروشی را که قسط یا پرداختی برایش رسیده نمی‌توان لغو کرد. هزینه‌های مرتبط، مثل کرایه، جلوی لغو را نمی‌گیرند و هزینه‌ی همان سفارش می‌مانند. اول همه‌ی قسط‌های دریافت‌شده را یکی‌یکی لغو کنید و پولشان را برگردانید، بعد خود فروش را لغو کنید. در نمونه فقط قسط سوم آمده است؛ قسط‌های اول و دوم هم به همین شکل لغو می‌شوند. اگر لغو فروش زودتر برسد، ثبت می‌شود اما اثری ندارد و در پنل ما نمایش داده می‌شود.

درخواست
POST /events
{
  "id": "case-450-inst-3-cancel",
  "type": "return",
  "of": "case-450-inst-3",
  "cancel": true,
  "occurred_at": "1405/09/02",
  "payments": [
    {
      "direction": "out",
      "amount": 50000000,
      "account": "acc-bank"
    }
  ]
}

POST /events
{
  "id": "case-450-cancel",
  "type": "return",
  "of": "case-450",
  "cancel": true,
  "occurred_at": "1405/09/02"
}

اصلاح اشتباه

رویداد ثبت‌شده ویرایش نمی‌شود، ولی اصلاح می‌شود. اگر مبلغ، نوع، تاریخ یا برچسب رویدادی اشتباه بوده، همان رویداد را با همان شناسه، کامل و درست، و با "edit": true دوباره بفرستید. یک درخواست کافی است.

نسخه‌ی قبلی، با پرداخت‌هایش، کامل برگردانده می‌شود و نسخه‌ی تازه جایش را می‌گیرد؛ پس چیزی دو بار حساب نمی‌شود. اگر ماهِ نسخه‌ی قبلی بسته شده باشد، تفاوت در ماه جاری ثبت می‌شود و ماه بسته دست نمی‌خورد. رویدادهای مرتبط، مثل کرایه‌ای که بعداً آمده، مانع اصلاح نمی‌شوند و به نسخه‌ی تازه منتقل می‌شوند.

فروش قطعی‌شده از طریق API اصلاح نمی‌شود؛ اگر برچسب یا مشخصات آن اشتباه است، به ما خبر بدهید تا در پنل درستش کنیم. در نمونه، قیمت این فروش ۱۲ میلیون ثبت شده بود و درستش ۱۰ میلیون است؛ برای همین حالتش recorded است.

درخواست
POST /events
{
  "id": "order-7790",
  "edit": true,
  "type": "sale",
  "state": "recorded",
  "occurred_at": "1405/08/15",
  "party": {
    "id": "cust-31",
    "name": "نرگس امینی"
  },
  "charges": [
    {
      "kind": "price",
      "amount": 10000000
    }
  ],
  "payments": [
    {
      "direction": "in",
      "amount": 10000000,
      "account": "acc-bank"
    }
  ]
}

خرید کالا برای انبار

کالایی که در item مقدار "stocked": true دارد، با خرید به انبار اضافه می‌شود. اگر هنوز پولش را نپرداخته‌اید، پرداخت را نفرستید؛ هر وقت پرداختید، یک رویداد تسویه با of بفرستید.

درخواست
POST /events
{
  "id": "po-2210",
  "type": "purchase_goods",
  "occurred_at": "1405/08/05",
  "party": {
    "id": "sup-ortino",
    "name": "اورتینو",
    "role": "supplier"
  },
  "lines": [
    {
      "item": {
        "id": "SUN-50",
        "name": "کرم ضدآفتاب ۵۰",
        "stocked": true
      },
      "qty": 40,
      "unit_price": 11000000
    }
  ],
  "charges": [
    {
      "kind": "purchase_price",
      "amount": 440000000
    }
  ]
}

مرجوعی بخشی از یک فروش

خریدار یکی از دو ماسک را پس می‌دهد. یک رویداد return بدون cancel و با of به فروش بفرستید. قلم برگشتی با "restock": true به انبار برمی‌گردد، reversal مبلغش را از قیمت کم می‌کند و پولی که برگردانده شده، پرداختی با جهت out است.

درخواست
POST /events
{
  "id": "order-10482-ret-1",
  "type": "return",
  "of": "order-10482",
  "occurred_at": "1405/08/14",
  "lines": [
    {
      "item": {
        "id": "MASK-02",
        "name": "ماسک صورت",
        "stocked": true
      },
      "qty": 1,
      "unit_price": 3000000,
      "restock": true
    }
  ],
  "charges": [
    {
      "kind": "reversal",
      "amount": 3000000
    }
  ],
  "payments": [
    {
      "direction": "out",
      "amount": 3000000,
      "account": "acc-zarinpal"
    }
  ]
}

هدیه، کالای خراب و کسری انبار

هدیه یا نمونه برای اینفلوئنسر، کالای خراب یا گم‌شده و کسری شمارش انبار را به‌صورت هزینه‌ی جاری، با قلم‌هایش و بدون مبلغ، بفرستید؛ ارزش کالا را خودمان حساب می‌کنیم. کالایی که برای مصرف خودتان برمی‌دارید از نوع owners_money_or_loan است، و مرجوعی‌ای که به انبار برگشته، return با "restock": true روی قلم.

اگر در شمارش انبار، موجودی کالایی بیشتر از دفتر بود، آن را نفرستید و به ما خبر بدهید. معمولاً یعنی خریدی ثبت نشده است و باید پیدایش کنیم.

درخواست
POST /events
{
  "id": "gift-0091",
  "type": "running_expense",
  "occurred_at": "1405/08/19",
  "party": {
    "id": "inf-31",
    "name": "صفحه‌ی زیبایی نمونه",
    "role": "reseller_or_influencer"
  },
  "description": "نمونه برای معرفی در صفحه",
  "lines": [
    {
      "item": {
        "id": "SUN-50",
        "name": "کرم ضدآفتاب ۵۰"
      },
      "qty": 2
    }
  ]
}

جابه‌جایی بین حساب‌های خودتان

مثلاً تسویه‌ی درگاه به حساب بانکی. دو پرداخت بفرستید، هر دو به مبلغی که واقعاً به بانک رسیده: یکی out از حساب مبدأ و یکی in به حساب مقصد.

درخواست
POST /events
{
  "id": "settle-zp-0412",
  "type": "own_transfer",
  "occurred_at": "1405/08/12",
  "payments": [
    {
      "direction": "out",
      "amount": 98000000,
      "account": "acc-zarinpal"
    },
    {
      "direction": "in",
      "amount": 98000000,
      "account": "acc-bank"
    }
  ]
}

پول صاحب کسب‌وکار و وام

پولی که صاحب کسب‌وکار یا شریک آورده، یا وامی که گرفته‌اید، پرداختی با جهت in است؛ پولی که برداشته‌اند یا قسط وامی که پس می‌دهید، out است. برای وام، نقش طرف حساب را partner_or_lender بگذارید و در توضیح بنویسید که وام است.

درخواست
POST /events
{
  "id": "owner-0007",
  "type": "owners_money_or_loan",
  "occurred_at": "1405/08/02",
  "party": {
    "id": "owner-1",
    "name": "صاحب کسب‌وکار",
    "role": "partner_or_lender"
  },
  "payments": [
    {
      "direction": "out",
      "amount": 30000000,
      "account": "acc-bank"
    }
  ]
}

شرح فنی (OpenAPI)

openapi.yamlدانلود
در حال بارگذاری…