قرارداد عمومی API
API نسخه ۲ برای دریافت JSON استاندارد، اعتبارسنجی شفاف مطابق دستورالعمل سامانه مؤدیان، ثبت مشتری، ثبت گروهی صورتحساب و پیگیری وضعیت طراحی شده است. خطاهای قابل کنترل با پیام قابل فهم، مسیر دقیق فیلد و traceId بازگردانده میشوند و جزئیات داخلی برنامه در پاسخ عمومی نمایش داده نمیشود.
جریان کاری پیشنهادی
برای پیادهسازی پایدار، مدیر هر کسبوکار باید client_id/client_secret را از بخش «اتصال API v2» در تنظیمات همان کسبوکار دریافت کند و سپس Access Token کوتاهمدت بگیرد. در ثبت مشتری، خروجی علاوه بر customerApiKey قدیمی، اطلاعات serviceAuth جدید را هم برمیگرداند.
/api/register
مشتری را ایجاد یا بهروزرسانی میکند و Customer API Key legacy و serviceAuth جدید را بازمیگرداند.
uuid + data[]
هر عضو data دارای internalId یکتا است و مستقل از سایر اعضا اعتبارسنجی و ذخیره میشود؛ سقف هر درخواست ۲۵۰ صورتحساب است.
/api/invoice/send
قبل از ذخیره، JSON و جمعهای مالی کنترل میشوند.
/api/invoice/inquiry*
وضعیت نهایی با internalId، no یا taxId پیگیری میشود.
/api/register را با Access Token دارای scope v2.register فراخوانی کنید. در دوره مهاجرت، Partner API Key قدیمی هم پذیرفته میشود.
data میتواند ۱ تا ۲۵۰ صورتحساب داشته باشد. هر عضو باید internalId یکتا، Header، Body و در صورت نیاز Payments داشته باشد.
data به همان ترتیب ورودی، نتیجه موفق یا خطای هر صورتحساب را برمیگرداند. خطای یک عضو مانع ثبت اعضای سالم نمیشود.
internalId، inno یا taxId آخرین وضعیت پردازش را دریافت کنید.
احراز هویت
روش اصلی احراز هویت API نسخه ۲ از این پس الگوی client_credentials است. کلیدهای ثابت قدیمی فقط برای دوره مهاجرت پذیرفته میشوند.
/apps/taxpayer-management/setting/{companyId}/api-v2 است. مقدار Client Secret فقط هنگام صدور یا چرخش نمایش داده میشود؛ همان لحظه آن را در Secret Manager سرور ذخیره کنید.
Authorization: Bearer <AccessToken>
Content-Type: application/json; charset=utf-8
| روش | محل استفاده | توضیح |
|---|---|---|
| Access Token | همه endpointها | توکن کوتاهمدت JWT که با client_id/client_secret گرفته میشود و بر اساس scope کنترل میشود. |
| Partner API Key legacy | /api/register | فقط تا تاریخ 2026-10-08T00:00:00Z برای فرصت مهاجرت فعال است. |
| Customer API Key legacy | /api/invoice/* | فقط تا تاریخ 2026-10-08T00:00:00Z برای فرصت مهاجرت فعال است. |
Partner API Key یا Customer API Key انجام میشود، تا تاریخ 2026-10-08T00:00:00Z قطع نمیشود. پاسخهای legacy هدرهای Deprecation، Sunset و Warning دارند. پیادهسازی جدید باید از Access Token استفاده کند.
دریافت یا چرخش client credentials
روش اصلی این است که مدیر کسبوکار از «پنل کنترل معتمد آسا ← کسبوکارها ← تنظیمات ← اتصال API v2» اعتبارنامه را صادر یا Client Secret را بچرخاند. Client ID همیشه در همان صفحه قابل مشاهده است، اما client_secret فقط یکبار نمایش داده میشود؛ آن را در secret store نگهداری کنید و در کد، مرورگر یا لاگ ثبت نکنید.
مسیر موقت مهاجرت برای اتصالهای قدیمی
اگر یک اتصال قدیمی از قبل API key معتبر دارد، تا پایان مهلت مهاجرت میتواند یک بار از endpoint زیر برای ساخت credential جدید استفاده کند. برای راهاندازیهای جدید از پنل کنترل استفاده کنید.
curl -X POST "https://api-v2.asatsp.ir/api/auth/v2/client-credentials" \
-H "Authorization: Bearer <PartnerApiKey | CustomerApiKey>" \
-H "Content-Type: application/json; charset=utf-8" \
-d '{
"credential_type": "customer",
"rotate_secret": false
}'
دریافت Access Token
توکنها کوتاهمدت هستند و مقدار پیشفرض انقضا ۶۰ دقیقه است. اگر scope ارسال نشود، همه scopeهای مجاز همان client استفاده میشود.
curl -X POST "https://api-v2.asatsp.ir/api/auth/v2/token" \
-H "Content-Type: application/json; charset=utf-8" \
-d '{
"grant_type": "client_credentials",
"client_id": "v2-customer-12345",
"client_secret": "<client_secret>",
"scope": "v2.invoice.send v2.invoice.read"
}'
| Scope | کاربرد |
|---|---|
v2.register | ثبت یا بهروزرسانی مشتری توسط پارتنر. |
v2.invoice.send | ثبت صورتحساب و ارسال دستی صورتحسابهای ثبتشده. |
v2.invoice.cancel | ابطال صورتحساب. |
v2.invoice.read | استعلام با internalId، شماره یا taxId. |
v2.lookup.read | خواندن لیست واحدها و ارزها. |
2026-10-08T00:00:00Z، API key ثابت در v2 پذیرفته نمیشود و فقط Access Token معتبر با scope لازم قابل استفاده است.
فهرست Endpointها
data.
unit و currencyCode.
internalId ارسالی.
inno.
ثبت یا بهروزرسانی مشتری
POST /api/register
این سرویس با Access Token دارای scope v2.register فراخوانی میشود و پس از اعتبارسنجی شناسه حافظه مالیاتی، مشتری را ایجاد یا بهروزرسانی میکند. در دوره مهاجرت، Partner API Key قدیمی هم پذیرفته میشود.
| فیلد | نوع | الزامی | توضیح |
|---|---|---|---|
type | number | بله | 1 حقیقی، 2 حقوقی، 3 مشارکت مدنی، 4 اتباع غیر ایرانی. |
fiscalId | string(6) | بله | شناسه حافظه مالیاتی مشتری که باید در همان محیط درخواست اخذ و قابل استعلام باشد. |
sandBox | boolean | خیر | true برای ثبت و استعلام در Sandbox؛ مقدار پیشفرض false و به معنی محیط عملیاتی است. |
economicCode | string | بله | شماره اقتصادی مشتری؛ با شناسه حافظه مالیاتی کنترل میشود. |
name | string | خیر | نام شخص یا شرکت. |
nationalCode | string | خیر | کد ملی یا شناسه ملی. |
mobile | string | خیر | شماره تلفن همراه. |
autoSend | boolean | خیر | در صورت عدم ارسال مقدار، پیشفرض true اعمال میشود. |
valueAddedNotCalled | boolean | خیر | معادل گزینه «مشمول فراخوان مالیات بر ارزش افزوده نیست» در پنل است. با مقدار true، نرخ و مبلغ مالیات بر ارزش افزوده صورتحسابها صفر کنترل میشود. در بهروزرسانی مشتری، ارسالنشدن این فیلد مقدار فعلی را تغییر نمیدهد. |
مثالهای چندزبانه ثبت مشتری
curl -X POST "https://api-v2.asatsp.ir/api/register" \
-H "Authorization: Bearer <AccessToken with v2.register>" \
-H "Content-Type: application/json; charset=utf-8" \
-d '{
"type": 2,
"fiscalId": "A1B2C3",
"economicCode": "12345678901234",
"name": "شرکت نمونه",
"nationalCode": "10100000000",
"mobile": "09120000000",
"autoSend": true,
"valueAddedNotCalled": true
}'
const response = await fetch("https://api-v2.asatsp.ir/api/register", {
method: "POST",
headers: {
"Authorization": "Bearer <AccessToken with v2.register>",
"Content-Type": "application/json; charset=utf-8"
},
body: JSON.stringify({
type: 2,
fiscalId: "A1B2C3",
economicCode: "12345678901234",
name: "شرکت نمونه",
nationalCode: "10100000000",
mobile: "09120000000",
autoSend: true,
valueAddedNotCalled: true
})
});
const result = await response.json();
using System.Net.Http;
using System.Text;
var json = """
{
"type": 2,
"fiscalId": "A1B2C3",
"economicCode": "12345678901234",
"name": "شرکت نمونه",
"nationalCode": "10100000000",
"mobile": "09120000000",
"autoSend": true,
"valueAddedNotCalled": true
}
""";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer <AccessToken with v2.register>");
var response = await client.PostAsync(
"https://api-v2.asatsp.ir/api/register",
new StringContent(json, Encoding.UTF8, "application/json"));
var body = await response.Content.ReadAsStringAsync();
import requests
payload = {
"type": 2,
"fiscalId": "A1B2C3",
"economicCode": "12345678901234",
"name": "شرکت نمونه",
"nationalCode": "10100000000",
"mobile": "09120000000",
"autoSend": True,
"valueAddedNotCalled": True,
}
response = requests.post(
"https://api-v2.asatsp.ir/api/register",
headers={"Authorization": "Bearer <AccessToken with v2.register>"},
json=payload,
timeout=30,
)
print(response.json())
$payload = [
"type" => 2,
"fiscalId" => "A1B2C3",
"economicCode" => "12345678901234",
"name" => "شرکت نمونه",
"nationalCode" => "10100000000",
"mobile" => "09120000000",
"autoSend" => true,
"valueAddedNotCalled" => true,
];
$ch = curl_init("https://api-v2.asatsp.ir/api/register");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <AccessToken with v2.register>",
"Content-Type: application/json; charset=utf-8",
],
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
]);
$result = curl_exec($ch);
curl_close($ch);
نمونه پاسخ سرویس ثبت مشتری
{
"result": 1,
"traceId": "6f9d0c76-1e78-4a61-9ec5-c63e38b49e3f",
"message": "مشتری با موفقیت ایجاد شد.",
"data": {
"companyId": 1250,
"fiscalId": "A1B2C3",
"economicCode": "12345678901234",
"valueAddedNotCalled": true,
"customerApiKey": "9a1b2c3d4e5f",
"serviceAuth": {
"credential_type": "customer",
"client_id": "v2-customer-1250",
"client_secret": "shown-only-once",
"allowed_scopes": "v2.invoice.cancel v2.invoice.read v2.invoice.send v2.lookup.read",
"token_endpoint": "/api/auth/v2/token",
"expires_in": 3600,
"legacy_api_key_sunset_utc": "2026-10-08T00:00:00Z"
},
"created": true,
"updated": false
}
}
ثبت صورتحساب با JSON استاندارد سازمان امور مالیاتی
POST /api/invoice/send
این سرویس با Access Token دارای scope v2.invoice.send فراخوانی میشود و برای ثبت صورتحساب با ساختار رسمی سازمان امور مالیاتی بهکار میرود. ورودی یک object شامل uuid و آرایه data با حداقل ۱ و حداکثر ۲۵۰ عضو است. فیلد uuid در ریشه بدنه، شناسه کل درخواست گروهی است و همه اعضای data[] همان درخواست، همین مقدار را به اشتراک میگذارند؛ برای هر درخواست جدید یک GUID تازه تولید کنید. هر عضو مستقل اعتبارسنجی و پردازش میشود؛ خطای یک صورتحساب مانع ثبت سایر صورتحسابهای سالم نیست.
data[] بازگردانده میشود. عضو ناموفق دارای status: 3 (hasError) و آرایه error است و مسیرهای ساختیافته خطا نیز در errors[].path قرار میگیرند.send اختیاری است. اگر این فیلد را ارسال نکنید، مقدار آن null باقی میماند و تنظیم ارسال خودکار «API نسخه ۲» کسبوکار تصمیم نهایی را تعیین میکند؛ فقط true یا false صریح این تنظیم را برای همان عضو بازنویسی میکند.Header.insr «قاعده ارسال صورتحساب» است و برای ماده ۹ فقط مقدار عددی 1 دارد. فیلد Header.indati2m «تاریخ و زمان ثبت صورتحساب» و یک Unix timestamp عددی با دقت میلیثانیه و حداکثر ۱۳ رقم است. مهلت فعلی سیستم ۱۵ روز کامل (۱۵ × ۲۴ ساعت) است و اعتبار نهایی هنگام ارسال بررسی میشود. اگر فاصله indatim تا زمان ارسال کمتر یا مساوی ۱۵ روز باشد، صورتحساب عادی است؛ حتی در صورت دریافت insr: 1، دو فیلد ماده ۹ در payload نهایی اعمال نمیشوند. برای صورتحساب خارج از این مهلت، insr: 1 و indati2m باید همزمان ارسال شوند، تاریخ ثبت نباید از indatim کوچکتر یا از زمان ارسال بزرگتر باشد و فاصله تاریخ ثبت تا ارسال نیز نباید بیش از ۱۵ روز باشد. این قابلیت برای صورتحسابهای نوع اول و دوم، همه الگوهای مجاز آنها و موضوعهای اصلی، اصلاحی، برگشت از فروش و ابطالی قابل استفاده است.
indati2m از تاریخ صدور indatim مستقل است و تاریخ صدور، TaxId، سریال یا FiscalId را تغییر نمیدهد. در صورتحساب عادی، insr و indati2m اختیاری و خارج از الگو هستند؛ درخواستهای قدیمی که این دو فیلد را ندارند بدون تغییر پشتیبانی میشوند.
{
"uuid": "2e4f2ce6-cc0e-447b-a697-204dc96ea6e8",
"data": [{
"internalId": "ARTICLE9-1001",
"send": true,
"fiscalId": "PRD123",
"sandBox": false,
"data": {
"Header": {
"indatim": 1782940800000,
"indati2m": 1784146800000,
"insr": 1,
"inty": 1,
"inp": 1,
"ins": 1,
"inno": "A000000009",
"tins": "12345678901234",
"tob": 2,
"tinb": "10987654321000",
"bpc": "1234567890",
"setm": 1,
"tprdis": 10000000,
"tdis": 0,
"tadis": 10000000,
"tvam": 1000000,
"todam": 0,
"tbill": 11000000,
"cap": 11000000,
"insp": 0,
"tvop": 1000000
},
"Body": [{
"sstid": "1234567890123",
"sstt": "کالای نمونه",
"mu": "162",
"am": 2,
"fee": 5000000,
"prdis": 10000000,
"dis": 0,
"adis": 10000000,
"vra": 10,
"vam": 1000000,
"odam": 0,
"olam": 0,
"tsstam": 11000000
}],
"Payments": []
}
}]
}
timestampها و شناسههای نمونه صرفاً برای نمایش قالب هستند؛ در درخواست واقعی، مقادیر متناظر با صورتحساب و زمان ثبت خود را ارسال کنید.
الگوی بورس اوراق بهادار مبتنی بر کالا (inp=11)
inty=1) و با دقیقاً یک ردیف در Body پذیرفته میشود. در Header، فیلدهای asn (شماره اعلامیه فروش بورس، فقط رقم و حداکثر ۳۰ رقم) و asd (تاریخ اعلامیه به روز Unix، حداکثر برابر روز تاریخ صدور) الزامیاند. در ردیف، mu، adis، vra، vam و tsstam الزامیاند؛ adis باید عیناً ارزش معامله در اعلامیه فروش بورس (ریال، عدد صحیح) باشد و vra و vam باید 0 باشند؛ بنابراین tsstam و tbill برابر adis است. در Header، tadis هم الزامی است و باید برابر adis تنها ردیف باشد. مبلغ واحد (fee) در این الگو خارج از الگوست؛ جزئیات در مبلغ واحد در الگوی ۱۱. cui (عیار) اختیاری است.
prdis، dis، Header.tprdis، Header.tdis، Header.todam و Payments در این الگو خارج از الگو هستند و نباید ارسال شوند؛ قاعده fee جداگانه در مبلغ واحد در الگوی ۱۱ آمده است. برای سازگاری، هر فیلد خارج از الگو با مقدار صفر یا رشته خالی (مانند dis، odam، olam، odr، cfee، odt، tdis، todam، tax17 یا crn)، آرایه خالی Payments، cui=0 و مقدار prdis/tprdis برابر با adis/tadis حذف میشود. مقدار غیرصفر یا غیرخالی فیلدهای خارج از الگو (تخفیف، سایر مالیات، فیلدهای ارزی، bsrn، crn، billid، tax17 و پرداختها) با خطای V79_OUT_OF_PATTERN رد میشوند. صورتحساب برگشت از فروش (ins=4) در این الگو مجاز نیست و در روش setm=3 جمع cap و insp باید برابر tbill باشد.
inp=11) فیلد fee (مبلغ واحد) خارج از الگو است؛ نه الزامی است و نه اختیاری، و هرگز به سامانه مؤدیان ارسال نمیشود. مبلغ تعیینکننده و الزامی این الگو adis (مبلغ بعد از تخفیف) است که باید دقیقاً برابر ارزش معامله در اعلامیه فروش بورس، به ریال و عدد صحیح بزرگتر از صفر باشد؛ لازم نیست بر am بخشپذیر باشد و گرد نمیشود.
feeرا ارسال نکنید. اگر کلاینت قدیمی آن را بفرستد، فعلاً برای سازگاری، مقدار صحیح و غیرمنفی آن (حتی0یا مقداری ناهمخوان باadisوam) در مبلغگذاری نادیده گرفته میشود و ذخیره نمیشود؛ ولی قالب آن همچنان کنترل میشود: مقدار اعشاری (مثلاً333333.67یعنیadis/am)، منفی یا غیرعددی خطایBody[i].feeمیگیرد و همان عضوdata[]ثبت نمیشود. با فعال شدن کنترل کامل Rule Matrix نسخه ۷.۹ برای الگوی ۱۱، هر مقدارfee(حتی0) با خطایV79_OUT_OF_PATTERNرد خواهد شد.adisرا نمیتوان حذف کرد: نبود یاnullبودنadisخطای الزامیبودن میگیرد و سامانه آن را ازfee × amیاprdis - disمحاسبه نمیکند.Header.tadisهم الزامی و برابرadisاست.- مبلغ واحد ذخیرهشده فقط نمایشی است: برای نمایش در پنل، چاپ و خروجیها و سازگاری با ساختار ردیف، سامانه یک مبلغ واحد را از روی
adisمحاسبه و ذخیره میکند: کوتاهترین عدد با حداکثر ۸ رقم اعشار کهfloor(مبلغ واحد × am)دقیقاً برابرadisشود؛ برایadis=1000001وam=3این مقدار333333.7است. این مقدار به سامانه مؤدیان ارسال نمیشود و در پاسخ سرویسهای ثبت و پیگیری API نسخه ۲ هم برنمیگردد. - تخفیف:
prdis،dis،Header.tprdisوHeader.tdisخارج از الگو هستند؛ مقدار صفر (وprdis/tprdisبرابرadis/tadis) حذف و هر مقدار دیگر باV79_OUT_OF_PATTERNرد میشود. جزئیات در جدول اعتبارسنجی JSON.
خلاصه رفتار مبلغ واحد و مبلغ بعد از تخفیف الگوی ۱۱ در مسیرهای API:
| مسیر | مبلغ واحد (fee / unitPrice) | مبلغ بعد از تخفیف (adis / amountAfterDiscount) | ارسال به سامانه مؤدیان |
|---|---|---|---|
| JSON استاندارد: /api/invoice/send | fee خارج از الگوست؛ ارسال نکنید. اگر ارسال شود باید عدد صحیح غیرمنفی باشد (مقدار اعشاری یا منفی خطا میگیرد)؛ سپس نادیده گرفته میشود و ذخیره نمیشود. | adis الزامی: عدد صحیح بزرگتر از صفر برابر ارزش معامله در اعلامیه فروش بورس؛ از fee یا prdis محاسبه نمیشود. Header.tadis برابر آن است. | adis و am عیناً؛ fee، prdis و dis ارسال نمیشوند. |
| مدل ساده: /api/invoice/commoditySecurities و /api/invoice/simple/type/1/template/11 | unitPrice فقط در نبود amountAfterDiscount لازم است و بهکار میرود؛ floor(qty × unitPrice) باید بزرگتر از صفر باشد (اعشار مجاز است). در کنار amountAfterDiscount نادیده گرفته میشود ولی نباید منفی باشد. | amountAfterDiscount (روش توصیهشده): عدد صحیح بزرگتر از صفر برابر ارزش معامله در اعلامیه فروش بورس؛ بر unitPrice مقدم است و عیناً adis میشود. | همان adis بهدستآمده و am؛ unitPrice ارسال نمیشود. |
| پاسخ پیگیری: inquiryInternalId، inquiryNo، inquiryTaxId و نسخههای آرایهای | برنمیگردد؛ پاسخ پیگیری فیلد ردیف ندارد و مبلغ واحد نمایشی (مثلاً 333333.7) فقط در پنل، چاپ و خروجی دیده میشود. | فقط در status: 2 و در json.Body[0].adis. | json همان payload ارسالشده است: در json.Body[0] adis و am مقدار دارند ولی fee، prdis و dis مقداری ندارند (برابر null هستند یا اصلاً نمیآیند). |
ins=2) برای زنجیره الگوی ۱۱ فقط تا زمانی مجاز است که اعلامیه فروش بورس تأیید نشده باشد؛ پس از تأیید فقط ابطال (ins=3) مجاز است. سامانه راهی برای دریافت خودکار این تأیید ندارد؛ وضعیت آن را با /api/invoice/exchangeAnnouncement یا در پنل ثبت کنید. تا وقتی وضعیتی ثبت نشده، اصلاحی مانند گذشته پذیرفته میشود. پس از ثبت «تأیید شده»، اصلاحی همان زنجیره (حتی اگر inp اصلاحی ۱۱ نباشد) با کد EXCHANGE_ANNOUNCEMENT_CONFIRMED، و اگر وضعیت موقتاً قابل خواندن نباشد با کد EXCHANGE_ANNOUNCEMENT_STATUS_UNAVAILABLE، در مسیر $.data[i] رد میشود. ابطال، برگشت از فروش و سایر اعضای درخواست گروهی تحت تأثیر قرار نمیگیرند.
{
"uuid": "6b1f0f3e-3c8a-4c61-9f7e-3f2d7a5b9c11",
"data": [{
"internalId": "IME-1001",
"send": true,
"fiscalId": "PRD123",
"sandBox": false,
"data": {
"Header": {
"indatim": 1788271140000,
"inty": 1,
"inp": 11,
"ins": 1,
"inno": "A000000011",
"tins": "12345678901234",
"tob": 2,
"tinb": "10987654321000",
"setm": 3,
"tadis": 1000001,
"tvam": 0,
"tbill": 1000001,
"cap": 400000,
"insp": 600001,
"asn": "1405012345",
"asd": 20697
},
"Body": [{
"sstid": "1234567890123",
"sstt": "کالای معاملهشده در بورس کالا",
"mu": "162",
"am": 3,
"adis": 1000001,
"vra": 0,
"vam": 0,
"tsstam": 1000001
}]
}
}]
}
در این نمونه adis=1000001 بر am=3 بخشپذیر نیست و همان مبلغ بدون گرد شدن بهعنوان مبلغ بعد از تخفیف ذخیره و ارسال میشود. fee ارسال نشده است؛ مبلغ واحد نمایشی که سامانه ذخیره میکند 333333.7 است (floor(333333.7 × 3) = 1000001) و به سامانه مؤدیان ارسال نمیشود. asd=20697 همان روز 2026/09/01 است که با روز تاریخ صدور برابر است.
برای اصلاحی الگوی ۱۱ (فقط پیش از تأیید اعلامیه فروش بورس؛ قاعده اصلاحی) همین ساختار را با ins=2، شماره مالیاتی صورتحساب مرجع در irtaxid، asn و asd اعلامیه و adis جدید بفرستید؛ در اصلاحی هم fee، prdis و dis خارج از الگو هستند و نباید ارسال شوند.
{
"uuid": "9a3e7c21-54b0-4f8e-a1d6-2c7b9e0f4a13",
"data": [{
"internalId": "IME-1001-EDIT",
"send": true,
"fiscalId": "PRD123",
"sandBox": false,
"data": {
"Header": {
"indatim": 1788274740000,
"inty": 1,
"inp": 11,
"ins": 2,
"irtaxid": "A1B2C304E7900000F42410",
"inno": "A000000012",
"tins": "12345678901234",
"tob": 2,
"tinb": "10987654321000",
"setm": 3,
"tadis": 1000500,
"tvam": 0,
"tbill": 1000500,
"cap": 400000,
"insp": 600500,
"asn": "1405012345",
"asd": 20697
},
"Body": [{
"sstid": "1234567890123",
"sstt": "کالای معاملهشده در بورس کالا",
"mu": "162",
"am": 3,
"adis": 1000500,
"vra": 0,
"vam": 0,
"tsstam": 1000500
}]
}
}]
}
ساختار کلی درخواست
{
"uuid": "GUID یکتای درخواست",
"data": [
{
"internalId": "کد داخلی یکتای صورتحساب",
"send": true,
"fiscalId": "SND123",
"sandBox": true,
"description": "توضیحات اختیاری",
"data": {
"Header": {},
"Body": [],
"Payments": []
}
}
]
}
آرایه data باید بین ۱ تا ۲۵۰ عضو داشته باشد و هر عضو مستقل اعتبارسنجی و ذخیره میشود.
uuid به کل درخواست تعلق دارد، نه به یک شرکت یا یک صورتحساب. کنترل تکراریبودن آن فقط در محدوده شرکت احراز هویتشده انجام میشود؛ بنابراین همان شرکت نباید uuid یک درخواست قبلی را دوباره استفاده کند. internalId نیز باید برای هر صورتحساب آن شرکت یکتا بماند.
Header.tins باید با کد اقتصادی شرکت احراز هویتشده برابر باشد.
چند شناسه حافظه مالیاتی و محیط Sandbox
هر کسبوکار میتواند چند شناسه حافظه مالیاتی داشته باشد. هر شناسه دقیقاً متعلق به یکی از محیطهای Production یا Sandbox است و هر صورتحساب هنگام ثبت به همان شناسه متصل میشود. Serial و TaxId بر اساس همان شناسه تولید میشوند؛ تغییر Default شرکت، شناسه یا اطلاعات صورتحسابهای قبلی را تغییر نمیدهد.
فیلدهای درخواست
| فیلد | نوع | الزام | قاعده |
|---|---|---|---|
fiscalId | string(6) | اختیاری | شناسه یکتای ششکاراکتری حافظه مالیاتی؛ باید متعلق به همان کسبوکار، فعال و با محیط درخواست سازگار باشد. در صورت ارسال، همان شناسه روی Invoice تثبیت میشود. |
sandBox | boolean | اختیاری | true برای Sandbox و false برای Production. مقدار پیشفرض false است؛ در صورت ارسالنشدن، درخواست عملیاتی محسوب میشود. |
https://sandboxrc.tax.gov.ir اخذ و در همان محیط استعلام شود. حتی اگر مقدار ششکاراکتری آن با شناسه عملیاتی یکسان باشد، ثبت Production و Sandbox دو رکورد مستقل هستند و انجام مراحل اخذ/فعالسازی در یک محیط، جایگزین محیط دیگر نیست.
انتخاب خودکار در نبود fiscalId
fiscalId ششکاراکتری الزامی است.نمونه درخواستها
{
"uuid": "0df9e8c6-07ba-4dc6-93f2-c20f5f4aef08",
"data": [{
"internalId": "PROD-1001",
"send": true,
"fiscalId": "PRD123",
"sandBox": false,
"data": { "Header": {}, "Body": [], "Payments": [] }
}]
}
{
"uuid": "8fd4439c-7964-4ac4-aee0-f6fd2dfadff8",
"data": [{
"internalId": "SANDBOX-1001",
"send": false,
"fiscalId": "SND123",
"sandBox": true,
"data": { "Header": {}, "Body": [], "Payments": [] }
}]
}
{
"uuid": "5ec152a0-a8e8-4ee1-9bbf-73f7954b38ee",
"data": [{
"internalId": "SANDBOX-1002",
"sandBox": true,
"data": { "Header": {}, "Body": [], "Payments": [] }
}]
}
{
"uuid": "17c0f06d-5bf1-40dc-b3cc-61b76677d26e",
"data": [{
"internalId": "LEGACY-1001",
"send": true,
"data": { "Header": {}, "Body": [], "Payments": [] }
}]
}
فیلدهای پاسخ
{
"fiscalMemoryId": 31,
"fiscalId": "ABC123",
"fiscalMemoryTitle": "شناسه تست API",
"sandBox": true,
"environment": "Sandbox",
"environmentTitle": "سندباکس",
"isDefaultFiscalMemory": true
}
خطاهای انتخاب شناسه
این پیامها عین پیامهای Validator مرکزی هستند و در خطای عضو مربوطه بازگردانده میشوند. برای جلوگیری از افشای مالکیت، «یافت نشدن» و «متعلقبودن به شرکت دیگر» یک پیام امن مشترک دارند.
| وضعیت | پیام واقعی API | اقدام اصلاحی |
|---|---|---|
| شناسه یافت نشد یا متعلق به شرکت دیگر است | FiscalId حافظه مالیاتی انتخابشده متعلق به این کسبوکار نیست. | یک fiscalId ششکاراکتری متعلق به شرکت احراز هویتشده ارسال کنید. |
| شناسه غیرفعال است | شناسه حافظه مالیاتی انتخابشده غیرفعال است و برای صورتحساب جدید قابل استفاده نیست. | شناسهای با IsEnabled = true انتخاب کنید. |
| محیط ناسازگار است | FiscalId حافظه مالیاتی انتخابشده در محیط عملیاتی/سندباکس این کسبوکار ثبت نشده است. | مقدار sandBox یا شناسه را اصلاح کنید؛ fallback بین محیطها انجام نمیشود. |
| چند شناسه بدون Default | در محیط عملیاتی/سندباکس چند شناسه حافظه مالیاتی فعال وجود دارد و هیچ شناسه پیشفرضی تعیین نشده است؛ شناسه حافظه مالیاتی باید صریح انتخاب شود. | Default همان محیط را تعیین یا fiscalId را صریح ارسال کنید. |
نمونههای خطا
در ثبت گروهی، خطای انتخاب شناسه در عضو متناظر data ثبت میشود. قطعههای زیر مقدار ورودی و پیام واقعی همان عضو را نشان میدهند.
{
"request": { "fiscalId": "ZZ9999", "sandBox": false },
"error": "FiscalId حافظه مالیاتی انتخابشده متعلق به این کسبوکار نیست."
}
{
"request": { "fiscalId": "OFF123", "sandBox": false },
"error": "شناسه حافظه مالیاتی انتخابشده غیرفعال است و برای صورتحساب جدید قابل استفاده نیست."
}
{
"request": { "fiscalId": "SND123", "sandBox": false },
"error": "FiscalId حافظه مالیاتی انتخابشده در محیط عملیاتی این کسبوکار ثبت نشده است."
}
{
"request": { "sandBox": true },
"error": "در محیط سندباکس چند شناسه حافظه مالیاتی فعال وجود دارد و هیچ شناسه پیشفرضی تعیین نشده است؛ شناسه حافظه مالیاتی باید صریح انتخاب شود."
}
isActive = false غیرفعال نیست. صورتحساب Sandbox فقط با شناسه Sandbox ثبت و به مقصد Sandbox ارسال میشود؛ شناسه Production در درخواست Sandbox و بالعکس پذیرفته نمیشود.رفتار ثبت گروهی، خطای جزئی و محدودیت درخواست
قواعد این بخش برای /api/invoice/send و همه Endpointهای مدل ساده آرایهای، از جمله /api/invoice/salesWithBuyerData، یکسان است.
| موضوع | رفتار API | اقدام پیشنهادی مصرفکننده |
|---|---|---|
| خطای اعتبارسنجی یک یا چند عضو | هر عضو مستقل اعتبارسنجی میشود. صورتحسابهای سالم ثبت میشوند و فقط اعضای نامعتبر با status: 3 و جزئیات error[] برمیگردند. | فقط اعضای ناموفق را پس از اصلاح، با uuid جدید دوباره ارسال کنید. |
| ترتیب پاسخ | آرایه پاسخ data دقیقاً با ترتیب آرایه ورودی متناظر است؛ هر ورودی یک نتیجه موفق یا ناموفق دارد. | برای تطبیق قطعی علاوه بر index، از internalId استفاده کنید. |
| خطای غیرمنتظره هنگام ذخیره | خطای اجرایی یک عضو ثبت اعضای بعدی را متوقف نمیکند و همان عضو با خطا برمیگردد. چون ذخیره هر صورتحساب مستقل است، نتیجه تکتک اعضا را بررسی کنید. | در timeout یا قطع ارتباط، پیش از retry وضعیت internalIdها را استعلام کنید. |
| خطای گذرای پایگاه داده | اگر ثبت یک عضو بهدلیل ازدحام یا بنبست پایگاه داده کامل نشود، همان عضو با status: 3 و خطایی با code: "INVOICE_DATABASE_TEMPORARILY_UNAVAILABLE" در errors[].path = $.data[i] برمیگردد. این خطا رد صورتحساب نیست و اعضای دیگر تحت تأثیر قرار نمیگیرند. | وضعیت همان internalId را با inquiryInternalId استعلام کنید و اگر ثبت نشده بود، فقط همان صورتحساب را در یک درخواست جدید با uuid جدید دوباره ارسال کنید. |
تعداد اعضای data | حداقل ۱ و حداکثر ۲۵۰ صورتحساب در هر درخواست مجاز است. درخواست دارای ۲۵۱ عضو یا بیشتر، پیش از پردازش کل آرایه رد میشود. | برای بیش از ۲۵۰ صورتحساب چند درخواست با uuidهای مستقل بسازید. |
| حجم و زمان درخواست | علاوه بر سقف ۲۵۰ عضو، محدودیت حجم JSON، وبسرور، پراکسی و timeout محیط استقرار نیز اعمال میشود. | حجم serialized JSON و timeout کلاینت را کنترل کنید. |
مقدار result | result: 1 یعنی حداقل یک صورتحساب ثبت شده است؛ حتی اگر تعدادی عضو خطا داشته باشند. result: 0 یعنی هیچ صورتحسابی ثبت نشده یا ساختار کلی درخواست رد شده است. | برای تشخیص موفقیت کامل، علاوه بر result، تمام اعضای data[] و errors[] را بررسی کنید. |
Header.tonw،
Header.sg یا Body.nw را در صورتحساب نوع اول یا دوم خارج از الگو بداند،
API آنها را پیش از اعتبارسنجی نادیده میگیرد. در صورتحساب نوع دوم همین رفتار برای
Header.setm، Header.cap، Header.insp، Header.tvop،
Body.cop و Body.vop نیز اعمال میشود.
این سازگاری فقط به همین فیلدهای امن محدود است و سایر فیلدهای خارج از الگو همچنان با خطای ساختیافته رد میشوند.
فیلد Header.bbc نادیده گرفته نمیشود و در صورت ارسال باید کد واقعی شعبه خریدار با دقیقاً ۴ رقم باشد.
قاعده تکرار امن درخواست
uuidرا برای هر درخواست جدید یکتا تولید کنید وinternalIdهر صورتحساب را ثابت و یکتا نگه دارید.- اگر پاسخ شامل نتیجه جزئی بود، اعضای دارای
status: 3را اصلاح کنید؛ اعضای موفق را دوباره نفرستید. - اگر
result: 0دریافت شد، هیچ عضوی ثبت نشده است؛ خطاهای ریشه یا تمام اعضای ناموفق را بررسی کنید. - اگر timeout، قطع ارتباط یا خطای اجرایی رخ داد، ابتدا با Endpointهای استعلام، وضعیت تکتک
internalIdها را بررسی کنید. - فقط صورتحسابهای ثبتنشده را با
uuidجدید دوباره ارسال کنید.
نمونههای چندزبانه ثبت صورتحساب
نمونه زیر ساختار یک درخواست گروهی دو عضوی را نشان میدهد. برای کوتاه ماندن نمونه، عضو دوم میتواند با همان ساختار عضو اول و با internalId و inno متفاوت ساخته شود.
{
"uuid": "9f806c1a-2c7e-4d1d-a3fa-2c65fd8c3210",
"data": [
{
"internalId": "1000001",
"send": true,
"fiscalId": "PRD123",
"sandBox": false,
"description": "فروش نقدی نمونه",
"data": {
"Header": {
"indatim": 1782940800000,
"inty": 1,
"inp": 1,
"ins": 1,
"inno": "A000000001",
"tins": "12345678901234",
"tob": 2,
"tinb": "10987654321000",
"bpc": "1234567890",
"setm": 1,
"tprdis": 10000000,
"tdis": 0,
"tadis": 10000000,
"tvam": 1000000,
"todam": 0,
"tbill": 11000000,
"cap": 11000000,
"insp": 0,
"tvop": 1000000
},
"Body": [
{
"sstid": "1234567890123",
"sstt": "کالای نمونه",
"mu": "162",
"am": 2,
"fee": 5000000,
"prdis": 10000000,
"dis": 0,
"adis": 10000000,
"vra": 10,
"vam": 1000000,
"odam": 0,
"olam": 0,
"tsstam": 11000000
}
],
"Payments": [
{
"pdt": 1782940800000,
"pmt": 2,
"pv": 11000000
}
]
}
},
{
"internalId": "1000002",
"send": false,
"fiscalId": "SND123",
"sandBox": true,
"description": "ثبت بدون صف ارسال خودکار",
"data": {
"Header": {
"indatim": 1782940800000,
"inty": 1,
"inp": 1,
"ins": 1,
"inno": "A000000002",
"tins": "12345678901234",
"tob": 2,
"tinb": "10987654321000",
"bpc": "1234567890",
"setm": 1,
"tprdis": 5000000,
"tdis": 0,
"tadis": 5000000,
"tvam": 500000,
"todam": 0,
"tbill": 5500000,
"cap": 5500000,
"insp": 0,
"tvop": 500000
},
"Body": [
{
"sstid": "1234567890123",
"sstt": "خدمت نمونه",
"mu": "162",
"am": 1,
"fee": 5000000,
"prdis": 5000000,
"dis": 0,
"adis": 5000000,
"vra": 10,
"vam": 500000,
"odam": 0,
"olam": 0,
"tsstam": 5500000
}
],
"Payments": [
{
"pdt": 1782940800000,
"pmt": 2,
"pv": 5500000
}
]
}
}
]
}
curl -X POST "https://api-v2.asatsp.ir/api/invoice/send" \
-H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
-H "Content-Type: application/json; charset=utf-8" \
--data-binary "@invoice-batch.json"
const payload = {
uuid: crypto.randomUUID(),
data: [
{
internalId: "1000001",
send: true,
fiscalId: "SND123",
sandBox: true,
description: "فروش نقدی نمونه",
data: invoiceDto
}
]
};
const response = await fetch("https://api-v2.asatsp.ir/api/invoice/send", {
method: "POST",
headers: {
"Authorization": "Bearer <AccessToken with v2.invoice.send>",
"Content-Type": "application/json; charset=utf-8"
},
body: JSON.stringify(payload)
});
const result = await response.json();
if (result.result !== 1) {
console.table(result.errors || []);
}
using System.Net.Http;
using System.Text;
var json = File.ReadAllText("invoice-batch.json");
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer <AccessToken with v2.invoice.send>");
var response = await client.PostAsync(
"https://api-v2.asatsp.ir/api/invoice/send",
new StringContent(json, Encoding.UTF8, "application/json"));
var body = await response.Content.ReadAsStringAsync();
import json
import requests
with open("invoice-batch.json", "r", encoding="utf-8") as file:
payload = json.load(file)
response = requests.post(
"https://api-v2.asatsp.ir/api/invoice/send",
headers={"Authorization": "Bearer <AccessToken with v2.invoice.send>"},
json=payload,
timeout=60,
)
result = response.json()
if result.get("result") != 1:
for error in result.get("errors", []):
print(error["path"], error["message"])
$payload = file_get_contents("invoice-batch.json");
$ch = curl_init("https://api-v2.asatsp.ir/api/invoice/send");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <AccessToken with v2.invoice.send>",
"Content-Type: application/json; charset=utf-8",
],
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 60,
]);
$result = curl_exec($ch);
curl_close($ch);
ثبت صورتحساب با مدل ساده و آرایهای
برای سامانههایی که نیاز ندارند JSON رسمی سامانه مؤدیان را بهصورت مستقیم تولید کنند، Endpointهای زیر مدل ورودی ساده و سازگار با نسخه ۱ را دریافت میکنند. بدنه همه این مسیرها یک object شامل uuid در ریشه و آرایه data با حداکثر ۲۵۰ عضو است. در این مدل نیز uuid شناسه کل درخواست است، بین همه اعضای data[] همان درخواست مشترک است و باید برای هر درخواست جدید مقدار تازهای داشته باشد. هر عضو باید internalId یکتا داشته باشد و مستقل پردازش میشود؛ خطای یک عضو مانع ثبت اعضای سالم نیست.
در تمام Endpointهای ساده، هر عضو data[] همانند مسیر /api/invoice/send فیلدهای send، fiscalId و sandBox را میپذیرد. اگر send حذف شود، مقدار آن null میماند و تنظیم ارسال خودکار «API نسخه ۲» کسبوکار اعمال میشود. برای ثبت سندباکس، sandBox: true و شناسه ششکاراکتری همان محیط را کنار send ارسال کنید؛ این انتخاب برای هر عضو جداگانه اعمال میشود. فیلدهای اختیاری isArticle9 و invoiceRegistrationDateTime نیز ماده ۹ و تاریخ ثبت Unix میلیثانیهای آن را مشخص میکنند.
ترکیبهای مجاز نوع و الگو از طریق مسیر عمومی /api/invoice/simple/type/{invoiceType}/template/{template} نیز قابل ثبت هستند. طبق دستورالعمل RC_IITP.IS V7.9، نوع اول الگوهای 1، 2، 3، 4، 5، 6، 7، 8، 9، 11، 13 و 14 را میپذیرد؛ نوع دوم فقط برای الگوهای 1، 3، 9 و 13 مجاز است.
| الگو | نوع اول | نوع دوم |
|---|---|---|
inp=1 فروش کالا و خدمات | /api/invoice/salesWithBuyerData | /api/invoice/salesEndUser |
inp=2 فروش ارز | /api/invoice/exchange | در نوع دوم مجاز نیست |
inp=3 طلا، جواهر و پلاتین | /api/invoice/jewelry | /api/invoice/jewelryEndUser |
inp=4 قرارداد پیمانکاری | /api/invoice/contracting | در نوع دوم مجاز نیست |
inp=5 قبوض خدماتی | /api/invoice/utilityBill | در نوع دوم مجاز نیست |
inp=6 بلیت هواپیما | /api/invoice/flightTicket | در نوع دوم مجاز نیست |
inp=7 صادرات | /api/invoice/export | در نوع دوم مجاز نیست |
inp=8 بارنامه | /api/invoice/billOfLading | در نوع دوم مجاز نیست |
inp=9 فروش فرآوردههای نفتی پالایش و پخش | /api/invoice/oilProducts | /api/invoice/oilProductsEndUser |
inp=11 بورس اوراق بهادار مبتنی بر کالا | /api/invoice/commoditySecurities | در نوع دوم مجاز نیست |
inp=13 بیمه | /api/invoice/insurance | /api/invoice/insuranceEndUser |
inp=14 فروش زنجیره | /api/invoice/chainSale | در نوع دوم مجاز نیست |
در مسیرهای نوع اول، ارسال اطلاعات خریدار در buyer الزامی است؛ تنها الگوی صادرات inp=7 از این الزام مستثنی است. در مسیرهای نوع دوم، اطلاعات خریدار اختیاری است.
قرارداد کامل اطلاعات خریدار (buyer)
شیء buyer در هر عضو data قرار میگیرد. نام فیلدها را مطابق جدول زیر ارسال کنید. همه شناسهها و شمارههای تماس باید از نوع JSON string باشند تا صفر ابتدای مقدار حذف نشود.
| فیلد | نوع | الزام | قالب و توضیح |
|---|---|---|---|
type | number | خیر؛ پیشفرض 1 | 1 حقیقی، 2 حقوقی، 3 مشارکت مدنی، 4 اتباع غیر ایرانی. برای جلوگیری از برداشت نادرست، ارسال صریح آن توصیه میشود. |
nationalCode | string | شرطی | فقط رقم؛ در قرارداد ورودی 5 تا 14 رقم. برای شخص حقیقی ایرانی کد ملی معتبر 10 رقمی و برای شخص حقوقی شناسه ملی 11 رقمی ارسال شود. در صورت نداشتن economicCode الزامی است. |
economicCode | string | برای نوع 2 الزامی | فقط رقم، 11 تا 14 رقم. حداقل یکی از nationalCode یا economicCode باید موجود باشد. |
zipCode | string | خیر | کد پستی 10 رقمی؛ بدون خط تیره یا فاصله. |
name | string | خیر | نام شخص یا عنوان خریدار. حداکثر ظرفیت ذخیرهسازی 4000 کاراکتر است. |
mobile | string | خیر | شماره همراه ایران با قالب پیشنهادی 09xxxxxxxxx و 11 رقم. مقدار معتبر به همین قالب نرمال میشود. |
address | string | خیر | نشانی پستی بهصورت متن Unicode، حداکثر 4000 کاراکتر. |
phone | string | خیر | شماره تلفن ثابت همراه پیششماره؛ الگوی عددی جداگانهای در endpoint اعمال نمیشود و حداکثر ظرفیت ذخیرهسازی 1024 کاراکتر است. |
branchCode | number | خیر | کد شعبه خریدار بهصورت عدد صحیح. |
passportNumber | string | شرطی | شماره گذرنامه خریدار غیرایرانی که کد فراگیر ندارد؛ حداکثر ۹ حرف و رقم لاتین. فقط با type=4 در الگوی فروش ارز (template=2) پذیرفته میشود و بهجای nationalCode بهعنوان header.bpn ارسال میشود. |
mobile، address و phone اطلاعات تکمیلی هستند و جایگزین شناسههای هویتی خریدار نمیشوند. برای خریدار حقوقی، economicCode حتی در صورت ارسال nationalCode همچنان الزامی است.
نمونه کامل buyer
"buyer": {
"type": 2,
"nationalCode": "10100000000",
"economicCode": "10987654321000",
"zipCode": "1234567890",
"name": "شرکت خریدار نمونه",
"mobile": "09120000000",
"address": "تهران، خیابان نمونه، پلاک ۱",
"phone": "02188770000",
"branchCode": 1
}
کد داخلی کالا/خدمت در مدل ساده
برای هر قلم در آرایه items میتوانید کد داخلی همان کالا یا خدمت را مطابق قرارداد زیر ارسال کنید.
| فیلد | نوع | الزام | قالب و توضیح |
|---|---|---|---|
items[].internalId | string | خیر | کد یا شناسه کالا/خدمت در سامانه مبدأ شما، حداکثر ۲۵۴ کاراکتر. API کالا/خدمت را با این کد شناسایی یا ایجاد میکند و مقدار را بهعنوان کد داخلی آن ذخیره میکند. برای حفظ صفرهای ابتدایی باید بهصورت JSON string ارسال شود. |
items[].internalId با data[].internalId (کد داخلی صورتحساب) و items[].stuffId تفاوت دارد. فیلد stuffId شناسه رسمی ۱۳رقمی کالا/خدمت است و به Body[].sstid نگاشت میشود؛ کد داخلی کالا/خدمت جایگزین آن نیست و در payload ارسالی به سامانه مودیان قرار نمیگیرد. برابر بودن اختیاری مقدار internalId و stuffId مجاز است. اگر internalId ارسال نشود، رفتار موجود سامانه برای تولید کد داخلی حفظ میشود.
مقادیر شمارشی مدل ساده
| فیلد | مقادیر مجاز | نکته |
|---|---|---|
invoiceSubject | 1 اصلی، 2 اصلاحی، 3 ابطالی، 4 برگشت از فروش | برای موضوعهای غیر اصلی، ارسال sourceInternalId یا sourceTaxId الزامی است. |
isArticle9 | true یا false (اختیاری) | در حالت true، فیلد invoiceRegistrationDateTime اجباری است؛ حذف فیلد یا false رفتار قبلی را حفظ میکند. |
invoiceRegistrationDateTime | Unix Time میلیثانیهای، حداکثر ۱۳ رقم | تاریخ و زمان ثبت صورتحساب موضوع ماده ۹؛ با تاریخ صدور dateTime متفاوت است. |
settlement | 1 نقدی، 2 نسیه، 3 نقدی/نسیه | در حالت 3، مقدار payment سهم نقدی تسویه است. |
buyer.type | 1 حقیقی، 2 حقوقی، 3 مشارکت مدنی، 4 اتباع غیر ایرانی | برای خریدار حقوقی، economicCode الزامی است. |
payments[].type | 1 چک، 2 تهاتر، 3 وجه نقد، 4 کارتخوان (POS)، 5 درگاه پرداخت اینترنتی، 6 کارتبهکارت، 7 انتقال به حساب، 8 سایر | این مقدار به Payments[].pmt نگاشت میشود. برای پرداخت کارتخوانی نوع 4، شماره پذیرنده، پایانه و کارت الزامی است. |
فیلدهای اختصاصی هر الگو
| الگو | فیلدهای ساده | توضیح |
|---|---|---|
inp=1 فروش | declarationNo, officeCode | اختیاری و فقط برای صورتحساب نوع اول: شماره پروانه گمرکی (header.scln، حداکثر 14 رقم) و کد گمرک محل اظهار (header.scc، دقیقاً 5 رقم). |
inp=2 ارز | items[].currencyCode, amountCurrency, currencyRate, currencyPurchaseRate, vatBaseAmount, declarationNo, officeCode, buyer.passportNumber | نرخ خرید ارز باید کوچکتر از نرخ فروش ارز باشد. declarationNo و officeCode مثل الگوی ۱. خریدار غیرایرانی فاقد کد فراگیر با buyer.type=4 و buyer.passportNumber ثبت میشود. |
inp=3 طلا، جواهر و پلاتین | items[].wage, profit, commission, carat | اجرت ساخت، سود فروشنده و حقالعمل الزامی و غیرمنفی هستند. |
inp=4 پیمانکاری | contractCode | شناسه یکتای ثبت قرارداد پیمانکاری، 12 رقم. |
inp=5 قبوض خدماتی | billNo | شناسه قبض یا شماره اشتراک بهرهبردار، حداکثر 19 رقم. |
inp=6 بلیت هواپیما | flightInfo | flightType: 1 داخلی، 2 خارجی. buyerMethod: 1 شماره ملی، 2 کد فراگیر اتباع، 3 گذرنامه. |
inp=7 صادرات | cottageNo, cottageDate, officeCode, items[].netWeight, currencyValue, currencyCode, currencyRate | اگر تاریخ کوتاژ ارسال شود، شماره کوتاژ هم الزامی است. officeCode (کد گمرک محل اظهار، header.scc) اختیاری و دقیقاً 5 رقم است؛ شماره پروانه گمرکی در این الگو خارج از الگوست. |
inp=8 بارنامه | shipmentInfo | شماره بارنامه، مبدأ/مقصد، فرستنده/گیرنده، نوع حمل، ناوگان و وزن کل کنترل میشوند. |
inp=9 فرآوردههای نفتی پالایش و پخش | فیلدهای عمومی کالا/خدمت و مالیات، items[].vatBaseAmount | vatBaseAmount مبلغ پایه مالیات بر ارزش افزوده (body[].vba) است؛ اگر بزرگتر از صفر باشد، مالیات ردیف از همین مبلغ محاسبه میشود و در غیر این صورت مبلغ بعد از تخفیف مبناست. |
inp=11 بورس اوراق بهادار مبتنی بر کالا | saleAnnNo, saleAnnDate, items[].amountAfterDiscount, items[].carat | شماره اعلامیه فروش باید عددی باشد و تاریخ اعلامیه الزامی است. تاریخ تقویمی بدون جابهجایی روز به asd تبدیل میشود؛ برای Unix time، روز تقویمی تهران مبناست. برای نمونه، 20260901 معادل 1405/06/10 و asd=20697 است. تاریخ اعلامیه نباید بعد از تاریخ صورتحساب dateTime باشد. items[].amountAfterDiscount ارزش معامله در اعلامیه فروش بورس به ریال (عدد صحیح بزرگتر از صفر) است و عیناً مبلغ بعد از تخفیف ردیف میشود؛ ترتیب اولویت: ابتدا amountAfterDiscount و در نبود آن floor(qty × unitPrice) که باید بزرگتر از صفر باشد. مبلغ واحد در این الگو خارج از الگوست (مبلغ واحد در الگوی ۱۱): unitPrice فقط وقتی لازم است که amountAfterDiscount ارسال نشود، میتواند اعشاری باشد، در کنار amountAfterDiscount نادیده گرفته میشود ولی در هر حال نباید منفی باشد، و به سامانه مؤدیان ارسال نمیشود. مبلغ واحد ذخیرهشده همیشه از مبلغ بعد از تخفیف محاسبه میشود، نه از unitPrice ارسالی؛ برای مثال qty=3 و unitPrice=333333.9 به adis=1000001 و مبلغ واحد نمایشی 333333.7 میرسد. فقط یک قلم مجاز است، discount غیرصفر با V79_OUT_OF_PATTERN رد میشود و vatRate باید 0 باشد یا ارسال نشود. carat (عیار) اختیاری است و 0 یعنی ارسالنشده؛ مقدار دیگر باید بزرگتر از صفر، حداکثر 1000 و حداکثر ۲ رقم اعشار. invoiceSubject=4، contractCode، billNo، tax17، payments، اطلاعات ارزی، سایر مالیاتها و contractNo در این الگو پذیرفته نمیشوند. |
inp=13 بیمه | insPolicyNo, extInsPolicyNo | شناسه بیمهنامه 9 تا 12 رقم است؛ شناسه الحاقیه اختیاری است. |
inp=14 فروش زنجیره | items | فقط یک قلم کالا یا خدمت مجاز است. |
نمونه ورودی ساده آرایهای
{
"uuid": "4d3d2921-6129-4a8b-9dc2-0ef08c2bf4fa",
"data": [
{
"internalId": "INV-10001",
"no": "A10001",
"dateTime": "20260705113000",
"isArticle9": false,
"invoiceSubject": 1,
"settlement": 1,
"send": true,
"fiscalId": "SND123",
"sandBox": true,
"buyer": {
"type": 2,
"nationalCode": "10100000000",
"economicCode": "10987654321000",
"zipCode": "1234567890",
"name": "شرکت خریدار نمونه",
"mobile": "09120000000",
"address": "تهران، خیابان نمونه، پلاک ۱",
"phone": "02188770000",
"branchCode": 1
},
"items": [
{
"internalId": "ITEM-1001",
"stuffId": "1234567890123",
"description": "کالای نمونه",
"qty": 2,
"unit": "162",
"unitPrice": 5000000,
"discount": 0,
"vatRate": 10
}
],
"payments": [
{
"type": 2,
"dateTime": "20260705113000",
"price": 11000000
}
]
}
]
}
نمونه فیلدهای الگوهای خاص
{
"flightTicket": {
"endpoint": "/api/invoice/flightTicket",
"addToEachDataItem": {
"flightInfo": {
"flightType": 1,
"buyerMethod": 1,
"nationalId": "0012345678",
"agentEconomicCode": "10987654321000"
}
}
},
"billOfLading": {
"endpoint": "/api/invoice/billOfLading",
"addToEachDataItem": {
"shipmentInfo": {
"blNumber": "123456789",
"originCountry": "364",
"originCity": "10000",
"destinationCountry": "364",
"destinationCity": "20000",
"senderEconomicCode": "0012345678",
"receiverEconomicCode": "10987654321",
"transportType": "1",
"fleetNumber": "IR-12-345",
"driverIdCode": "0012345678",
"totalWeight": 1250,
"shippedItems": [
{ "stuffId": "1234567890123", "name": "کالای حملشده" }
]
}
}
},
"insurance": {
"endpoint": "/api/invoice/insurance",
"typeTwoEndpoint": "/api/invoice/insuranceEndUser",
"addToEachDataItem": {
"insPolicyNo": "123456789",
"extInsPolicyNo": "987654321"
}
},
"oilProducts": {
"endpoint": "/api/invoice/oilProducts",
"typeTwoEndpoint": "/api/invoice/oilProductsEndUser"
},
"commoditySecurities": {
"endpoint": "/api/invoice/commoditySecurities",
"addToEachDataItem": {
"saleAnnNo": "1405012345",
"saleAnnDate": "20260705"
}
}
}
نمونه کامل مدل ساده برای الگوی بورس (/api/invoice/commoditySecurities)
invoiceSubject: 2) در این الگو هم تابع وضعیت تأیید اعلامیه فروش بورس است: پس از ثبت «تأیید شده» با /api/invoice/exchangeAnnouncement یا در پنل، اصلاحی با کد EXCHANGE_ANNOUNCEMENT_CONFIRMED در مسیر $.data[i] رد میشود و فقط ابطال مجاز است. جزئیات در قاعده اصلاحی الگوی ۱۱.
این نمونه همان مسیر /api/invoice/simple/type/1/template/11 را هم پوشش میدهد. saleAnnDate میتواند yyyyMMdd، yyyyMMddHHmmss، Unix time یا تاریخ شمسی 1405/04/14 باشد و به همان روز تقویمی ذخیره و ارسال میشود. در روش تسویه 3، مقدار payment سهم نقدی است. مطابق نمونه، ارزش معامله در اعلامیه فروش بورس را در amountAfterDiscount بفرستید و unitPrice را حذف کنید؛ مبلغ واحد نمایشی از همین مبلغ محاسبه میشود (جدول مبالغ الگوی ۱۱).
{
"uuid": "0c7f5f0e-8a3d-4a53-9f5c-1d2b3c4d5e6f",
"data": [
{
"internalId": "IME-2001",
"no": "A20001",
"dateTime": "20260705113000",
"invoiceSubject": 1,
"settlement": 3,
"payment": 400000,
"send": false,
"fiscalId": "SND123",
"sandBox": true,
"saleAnnNo": "1405012345",
"saleAnnDate": "20260705",
"buyer": { "type": 2, "economicCode": "10987654321000", "zipCode": "1234567890" },
"items": [
{
"stuffId": "1234567890123",
"description": "کالای معاملهشده در بورس کالا",
"qty": 3,
"unit": "162",
"amountAfterDiscount": 1000001,
"vatRate": 0
}
]
}
]
}
نمونه اصلاحی یا ابطالی
{
"uuid": "2df1926a-486c-4dd7-9b02-882e1e5a5c4a",
"data": [
{
"internalId": "INV-10001-EDIT",
"sourceInternalId": "INV-10001",
"no": "A10002",
"dateTime": "20260705123000",
"invoiceSubject": 2,
"settlement": 1,
"send": false,
"fiscalId": "SND123",
"sandBox": true,
"buyer": { "type": 2, "economicCode": "10987654321000", "zipCode": "1234567890" },
"items": [
{ "stuffId": "1234567890123", "description": "کالای اصلاحشده", "qty": 2, "unit": "162", "unitPrice": 5200000, "vatRate": 10 }
]
}
]
}
invoiceSubject: 2 و sourceInternalId را همراه saleAnnNo، saleAnnDate و amountAfterDiscount جدید بفرستید؛ unitPrice لازم نیست، vatRate باید 0 باشد یا ارسال نشود و discount غیرصفر پذیرفته نمیشود. این اصلاحی فقط تا پیش از تأیید اعلامیه فروش بورس مجاز است (قاعده اصلاحی الگوی ۱۱).
ارسال دستی به سازمان امور مالیاتی و ثبت صورتحساب ابطالی
| مسیر | بدنه | خروجی |
|---|---|---|
| POST /api/invoice/sendInvoice | { "internalIds": ["INV-10001"] } | صورتحسابهای ثبتشده بهصورت دستی در صف ارسال به سازمان امور مالیاتی قرار میگیرند. برای هر internalId، آخرین صورتحساب زنجیره آن در نظر گرفته میشود؛ یعنی اگر برای آن اصلاحی، ابطالی یا برگشت از فروش ثبت شده باشد (حتی ارسالنشده)، همان آخرین صورتحساب در صورت ارسالنشده بودن در صف ارسال قرار میگیرد و اگر قبلاً ارسال شده باشد فقط وضعیت آن برمیگردد. اگر حتی یک شناسه خطا بگیرد، پاسخ با result: 0 فقط errors دارد و data ندارد، هرچند شناسههای سالم همان درخواست در صف ارسال قرار گرفتهاند؛ وضعیت آنها را با سرویسهای پیگیری بررسی کنید. |
| POST /api/invoice/cancelInvoice | internalId مرجع، uuid و در صورت نیاز cancelInternalId | صورتحساب ابطالی ثبت میشود؛ مقدار صریح send اولویت دارد و در نبود آن، تنظیم ارسال خودکار «API نسخه ۲» کسبوکار اعمال میشود. ابطال برای آخرین صورتحساب زنجیره internalId ثبت میشود و آن صورتحساب باید ارسالشده و دارای شماره مالیاتی باشد؛ اگر آخرین صورتحساب زنجیره یک اصلاحی ارسالنشده یا هنوز بینتیجه باشد، درخواست با خطا در مسیر $.internalId رد میشود. |
{
"uuid": "74ff263d-bf33-40df-a56d-91f07bb1624b",
"internalId": "INV-10001",
"cancelInternalId": "INV-10001-CANCEL",
"send": true,
"description": "ابطال به درخواست مشتری"
}
دادههای مرجع
| مسیر | احراز هویت | کاربرد |
|---|---|---|
| GET /api/InvoiceItemUnit | v2.lookup.read | دیکشنری UnitId => Name برای انتخاب واحد اندازهگیری مجاز. |
| GET /api/Currency | v2.lookup.read | دیکشنری Code => Name برای انتخاب ارزهای مجاز. |
اعتبارسنجی JSON مطابق دستورالعمل
اعتبارسنجی پیش از تبدیل نهایی و ذخیره انجام میشود. مسیر خطاها با قراردادی مشابه JSONPath بازگردانده میشود؛ برای مثال $.data[1].data.Header.tbill به فیلد tbill در صورتحساب دوم اشاره دارد.
| فیلد | قاعده | نمونه خطا |
|---|---|---|
$ | بدنه باید JSON object معتبر باشد. property تکراری پذیرفته نمیشود. | ساختار JSON معتبر نیست. |
uuid | در ریشه درخواست؛ رشته غیرخالی و GUID معتبر. شناسه کل درخواست گروهی است و برای هر درخواست جدید باید مقدار تازهای داشته باشد. | این uuid قبلاً در درخواست دیگری از شرکت احراز هویتشده ثبت شده است؛ محدوده شرکت فقط برای کنترل تکراریبودن استفاده میشود. |
data | آرایه شامل ۱ تا ۲۵۰ صورتحساب؛ هر عضو مستقل اعتبارسنجی و پردازش میشود. | بیشتر از ۲۵۰ عضو باعث رد کامل درخواست پیش از پردازش میشود. |
data[].internalId | رشته غیرخالی، یکتا داخل همان درخواست گروهی و یکتا برای شرکت. | internalId ارسالی قبلاً برای این شرکت ثبت شده است. |
data[].send | اختیاری و در صورت ارسال باید boolean باشد. | true ارسال خودکار و false ثبت بدون ارسال را برای همان عضو قطعی میکند؛ در نبود فیلد، تنظیم «API نسخه ۲» کسبوکار اعمال میشود. |
data[].data | object شامل Header و Body. | فیلد data الزامی است. |
| فیلد | قاعده | توضیح |
|---|---|---|
indatim | Unix time عددی بر حسب میلیثانیه، حداکثر 13 رقم. | تاریخ و زمان صدور صورتحساب. |
indati2m | تاریخ و زمان ثبت صورتحساب؛ Unix time عددی بر حسب میلیثانیه، حداکثر ۱۳ رقم و بدون اعشار. | فقط در ماده ۹ و همراه insr=1 لازم است؛ باید مستقل از indatim و بزرگتر یا مساوی آن باشد، از زمان ارسال جلوتر نباشد و فاصله آن تا ارسال بیش از ۱۵ روز نباشد. |
insr | قاعده ارسال صورتحساب؛ در حالت ماده ۹ مقدار عددی 1. | برای صورتحساب خارج از مهلت ۱۵ روز همراه indati2m لازم است. برای صورتحساب عادی داخل مهلت، خارج از الگو است و در payload نهایی اعمال نمیشود. |
inty | 1 یا 2 | نوع صورتحساب؛ در صورتحساب ابطالی نسخه ۷.۹ ارسال نمیشود و از مرجع دریافت میشود. |
inp | 1، 2، 3، 4، 5، 6، 7، 8، 9، 11، 13 یا 14 | الگوی صورتحساب؛ در صورتحساب ابطالی نسخه ۷.۹ ارسال نمیشود و از مرجع دریافت میشود. |
inty + inp | نوع اول: همه الگوهای مجاز؛ نوع دوم: 1، 3، 9، 13 | ترکیب نوع و الگو پیش از ثبت کنترل میشود و برای ترکیب نامعتبر خطای ساختیافته برمیگردد. |
ins | 1 اصلی، 2 اصلاحی، 3 ابطالی، 4 برگشت از فروش | برای غیر اصلی، irtaxid الزامی است. |
inno | رشته شامل حروف و اعداد انگلیسی، حداکثر 10 کاراکتر. | سریال داخلی حافظه مالیاتی. |
tins | شماره اقتصادی فروشنده، 11 تا 14 رقم. | باید با شرکت احراز هویتشده برابر باشد. |
tob | 1 حقیقی، 2 حقوقی، 3 مشارکت مدنی، 4 اتباع غیر ایرانی | نوع شخص خریدار؛ برای صورتحساب نوع دوم و الگوی صادرات اختیاری و برای صورتحساب ابطالی خارج از الگو است. |
tinb | 11 تا 14 رقم. | در صورت ارسال خریدار حقوقی الزامی است؛ در الگوی صادرات اختیاری و در صورتحساب ابطالی خارج از الگو است. |
bid | 5 تا 14 رقم. | برای نوع اول بهجز الگوی صادرات، حداقل tinb یا bid لازم است؛ در صورتحساب ابطالی خارج از الگو است. |
bpc | در صورت ارسال دقیقاً 10 رقم. | کد پستی خریدار. |
setm | 1 نقدی، 2 نسیه، 3 نقدی/نسیه | روش تسویه. |
cap | عدد ریالی غیرمنفی. | برای روش نقدی و نقدی/نسیه الزامی است. در الگوی بورس inp=11 با روش نقدی/نسیه (setm=3)، همین مقدار سهم نقدی صورتحساب است و ذخیره میشود. |
insp | عدد ریالی غیرمنفی. | برای روش نسیه و نقدی/نسیه الزامی است. |
tprdis, tdis, tadis, tvam, todam, tbill | جمعهای Header باید با مجموع Body برابر باشند. | برای الگوی صادرات برخی جمعها اختیاریاند. در الگوی بورس inp=11، tprdis، tdis و todam خارج از الگو هستند و tadis الزامی و برابر adis است. |
crn | برای الگوی پیمانکاری الزامی. | شماره قرارداد. |
billid | برای الگوی قبوض خدماتی الزامی. | شناسه قبض. |
bpn | حداکثر ۹ حرف و رقم لاتین. | در الگوی فروش ارز (inp=2) برای خریدار غیرایرانی فاقد کد فراگیر: tob=4 و bpn بدون bid؛ خریدار با همین شماره گذرنامه ثبت میشود. |
scln, scc | scln حداکثر 14 رقم؛ scc دقیقاً 5 رقم. | شماره پروانه گمرکی (نوع اول، الگوهای ۱ و ۲) و کد گمرک محل اظهار (نوع اول، الگوهای ۱، ۲ و ۷)؛ ذخیره و به سامانه مؤدیان ارسال میشوند. مقدار خارج از این قالب نادیده گرفته میشود. |
cdcn, cdcd | cdcn حداکثر 14 رقم؛ cdcd روز Unix. | شماره و تاریخ کوتاژ اظهارنامه گمرکی در الگوی صادرات (inp=7). |
in, an | 9 تا 12 رقم. | شناسه یکتای بیمهنامه (الزامی) و الحاقیه در الگوی بیمه (inp=13). |
Body[].vba | عدد ریالی غیرمنفی. | مبلغ پایه مالیات بر ارزش افزوده در الگوی ۹؛ مقدار بزرگتر از صفر ذخیره و ارسال میشود. |
asn | رشته فقط شامل رقم، ۱ تا ۳۰ رقم. | شماره اعلامیه فروش بورس؛ در الگوی بورس اوراق بهادار مبتنی بر کالا inp=11 برای صورتحساب اصلی و اصلاحی الزامی است. |
asd | عدد صحیح مثبت: تعداد روز از 1970/01/01 (Unix day؛ نه میلیثانیه). | تاریخ اعلامیه فروش بورس؛ در inp=11 الزامی است و نباید بعد از روز تقویمی تاریخ صدور indatim به وقت تهران باشد. برای نمونه، 20697 یعنی 2026/09/01 (1405/06/10). |
inp=11 | فقط نوع اول، یک ردیف، بدون برگشت از فروش (ins=4). | tprdis، tdis و todam خارج از الگو هستند؛ مقدار صفر آنها (و tprdis برابر tadis) حذف و مقدار دیگر رد میشود. سایر فیلدهای خارج از الگو (از جمله crn، billid و tax17) با مقدار صفر یا رشته خالی حذف میشوند و با مقدار دیگر پذیرفته نمیشوند. tadis الزامی است و باید برابر adis تنها ردیف باشد. در setm=3 جمع cap و insp باید برابر tbill باشد. نمونه کامل و قاعده مبلغ واحد در بخش الگوی ۱۱ آمده است. |
| فیلد | قاعده | توضیح |
|---|---|---|
Body | آرایه شامل حداقل یک قلم. | در الگوی بورس اوراق بهادار مبتنی بر کالا inp=11 و الگوی فروش زنجیره inp=14 فقط یک ردیف مجاز است؛ در صورتحساب ابطالی نسخه ۷.۹ کل Body حذف و اطلاعات از مرجع دریافت میشود. |
sstid | رشته 13 رقمی. | شناسه کالا/خدمت. |
sstt | رشته حداکثر 400 کاراکتر، اختیاری. | شرح کالا/خدمت. |
mu | کد عددی واحد اندازهگیری. | در صورت ارسال باید فقط رقم باشد؛ در الگوی بورس inp=11 الزامی است. |
am | عدد مثبت. | تعداد/مقدار. |
fee | عدد ریالی صحیح و غیرمنفی (بدون اعشار). | مبلغ واحد؛ در همه الگوها بهجز فروش ارز inp=2 (اختیاری) و بورس inp=11 الزامی است. مقدار اعشاری یا منفی در هر الگویی، حتی وقتی فیلد لازم نیست، خطا میگیرد. در الگوی بورس inp=11 خارج از الگوست: ارسال نکنید؛ مقدار معتبر ارسالی نادیده گرفته میشود، ذخیره نمیشود و به سامانه مؤدیان ارسال نمیشود، و با فعال شدن کنترل کامل Rule Matrix نسخه ۷.۹ برای این الگو با V79_OUT_OF_PATTERN رد خواهد شد. جزئیات در مبلغ واحد در الگوی ۱۱. |
prdis | عدد ریالی غیرمنفی. | مبلغ قبل از تخفیف. در الگوی صادرات inp=7 اختیاری است؛ در الگوی بورس inp=11 خارج از الگوست؛ مقدار صفر یا برابر با adis حذف و هر مقدار دیگر رد میشود و adis از آن محاسبه نمیشود. |
dis | عدد ریالی غیرمنفی، پیشفرض صفر. | تخفیف. در الگوی بورس inp=11 خارج از الگوست؛ مقدار صفر حذف و مقدار غیرصفر رد میشود. |
adis | برابر prdis - dis؛ در صورت ارسالنشدن از همین رابطه محاسبه میشود. در الگوی بورس inp=11 مقدار مستقل و الزامی است و از prdis، dis یا fee محاسبه نمیشود. | مبلغ بعد از تخفیف. در الگوی بورس inp=11 باید دقیقاً ارزش معامله در اعلامیه فروش بورس (ریال، عدد صحیح بزرگتر از صفر) باشد؛ همین مقدار عیناً ذخیره و ارسال میشود و لازم نیست بر am بخشپذیر باشد. مبلغ واحد نمایشی ذخیرهشده از همین مقدار محاسبه میشود و ارسال نمیشود (مبلغ واحد در الگوی ۱۱). |
vra | عدد بین 0 تا 100. | نرخ مالیات بر ارزش افزوده. در الگوی بورس inp=11 باید 0 باشد. |
vam | عدد ریالی غیرمنفی. | مبلغ مالیات بر ارزش افزوده. در الگوی بورس inp=11 باید 0 باشد. |
odam, olam | عدد ریالی غیرمنفی؛ در صورت عدم ارسال، صفر در نظر گرفته میشود. | سایر مالیات/عوارض و وجوه قانونی. در الگوی بورس inp=11 خارج از الگوست؛ مقدار صفر حذف و مقدار غیرصفر رد میشود. |
tsstam | برابر adis + vam + odam + olam. | مبلغ کل ردیف. |
cfee, cut, exr | اگر اطلاعات ارزی ارسال شود، کنترل میشوند. | میزان ارز، نوع ارز و نرخ برابری. در الگوی بورس inp=11 خارج از الگوست؛ مقدار صفر یا خالی حذف و مقدار دیگر رد میشود. |
consfee, spro, bros, tcpbs | برای طلا/جواهر inp=3 الزامی است. | اجرت، سود، حقالعمل و جمع آنها. |
nw, ssrv, sscv | برای صادرات inp=7 الزامی. | وزن خالص، ارزش ریالی و ارزش ارزی. |
cui | اختیاری؛ بزرگتر از صفر، حداکثر 1000 و حداکثر ۲ رقم اعشار. | عیار در الگوی بورس inp=11؛ مقدار 0 یعنی ارسالنشده و حذف میشود. |
| فیلد | قاعده | توضیح |
|---|---|---|
Payments | اختیاری؛ در صورت ارسال باید array باشد. | جزئیات پرداخت. در الگوی بورس inp=11 خارج از الگوست؛ فیلد را حذف کنید یا آرایه خالی بفرستید. |
pmt | عدد صحیح یکی از مقادیر 1 تا 8. | 1 چک، 2 تهاتر، 3 وجه نقد، 4 کارتخوان (POS)، 5 درگاه پرداخت اینترنتی، 6 کارتبهکارت، 7 انتقال به حساب، 8 سایر. |
pdt | Unix time عددی بر حسب میلیثانیه. | تاریخ پرداخت. |
pv | عدد ریالی غیرمنفی. | نباید از tbill بیشتر شود. |
acn | 14 رقم. | برای pmt=4 الزامی. |
trmn | 8 رقم. | برای pmt=4 الزامی. |
pcn | 16 رقم. | برای pmt=4 الزامی. |
iinn | 9 رقم. | شماره سوئیچ پرداخت، اختیاری. |
trn | 1 تا 14 رقم. | شماره پیگیری یا مرجع، اختیاری. |
pid | 1 تا 12 رقم. | شناسه پرداختکننده، اختیاری. |
| کنترل | قاعده | مسیر خطا |
|---|---|---|
| مالکیت فروشنده | Header.tins برابر کد اقتصادی شرکت صاحب کلید. | $.data[n].data.Header.tins |
| جمع قبل از تخفیف | Header.tprdis = sum(Body[].prdis) | $.data[n].data.Header.tprdis |
| جمع تخفیف | Header.tdis = sum(Body[].dis) | $.data[n].data.Header.tdis |
| جمع بعد از تخفیف | Header.tadis = sum(Body[].adis) | $.data[n].data.Header.tadis |
| جمع ارزش افزوده | Header.tvam = sum(Body[].vam) | $.data[n].data.Header.tvam |
| جمع کل | Header.tbill = sum(Body[].tsstam) | $.data[n].data.Header.tbill |
| تسویه | cap + insp <= tbill در صورت ارسال cap یا insp. | $.data[n].data.Header.cap |
| پرداختها | مجموع Payments[].pv نباید از tbill بیشتر باشد. | $.data[n].data.Payments |
نکات مهم ساختار داده
| موضوع | توضیح | پیشنهاد پیادهسازی |
|---|---|---|
| مبالغ ریالی | فیلدهای مبلغی باید بهصورت عدد غیرمنفی و بدون اعشار ارسال شوند. | محاسبات را پیش از serialization نهایی گرد و کنترل کنید. |
| تاریخها | indatim، indati2m و pdt Unix time بر حسب میلیثانیه هستند. indati2m فقط تاریخ ثبت ماده ۹ است و جایگزین indatim نمیشود. | ارسال timestamp ثانیهای باعث خطا میشود. |
| حساسیت نام فیلدها | نام فیلدها در سمت API بهصورت case-insensitive پردازش میشوند. | برای هماهنگی با نمونهها از Header، Body و Payments استفاده کنید. |
| ارسال خودکار | مقدار صریح send برای هر صورتحساب بر تنظیم کسبوکار اولویت دارد؛ در نبود آن، تنظیم مستقل «API نسخه ۲» استفاده میشود. | برای رفتار قطعی همان درخواست، مقدار send: true یا send: false را ارسال کنید. |
| درخواست گروهی | هر عضو data مستقل اعتبارسنجی و ذخیره میشود و پاسخ ۱:۱ به ترتیب ورودی برمیگردد. سقف هر درخواست ۲۵۰ عضو است. | فقط اعضای دارای status: 3 را پس از اصلاح و با uuid جدید دوباره ارسال کنید. |
خطاها و پاسخها
در خطاهای قابل کنترل، API پاسخ JSON شامل result، traceId، message و در صورت وجود errors[] بازمیگرداند. مقدار traceId را برای پیگیری پشتیبانی نگهداری کنید.
internalId و traceId در آن ثبت میشود و با جستوجوی هر کدام از این مقادیر پیدا میشود. برای دیدن علت رد یک صورتحساب، پیش از تماس با پشتیبانی همین صفحه را ببینید.| result | معنا | نمونه وضعیت |
|---|---|---|
-2 | درخواست تکراری | uuid این درخواست قبلاً در محدوده شرکت احراز هویتشده ثبت شده و کل درخواست رد شده است. |
-1 | احراز هویت نامعتبر | هدر Authorization ارسال نشده، Access Token منقضی یا scope لازم وجود ندارد. |
0 | هیچ صورتحسابی ثبت نشده | ساختار ریشه یا uuid نامعتبر است، تعداد اعضا بیشتر از ۲۵۰ است، یا تمام اعضای آرایه خطا دارند. |
1 | حداقل یک ثبت موفق | ممکن است همه اعضا موفق باشند یا پاسخ جزئی شامل اعضای موفق و اعضای دارای status: 3 باشد. |
2، 3 | ثبت مشتری | exists یا edit در پاسخ سرویس ثبت مشتری. |
کدهای خطای اعلامیه فروش بورس (الگوی ۱۱)
این کدها در errors[].code برمیگردند. پاسخ HTTP مانند سایر سرویسها 200 است؛ در سرویسهای وضعیت اعلامیه، result برابر 0 است و در ثبت گروهی فقط همان عضو خطا میگیرد. سایر خطاها بدون code و مانند گذشته برمیگردند.
| code | path | معنا و اقدام |
|---|---|---|
EXCHANGE_ANNOUNCEMENT_CONFIRMED | $.data[i] یا در sendInvoice مسیر $.internalIds[i] | اعلامیه فروش بورس این زنجیره تأیید شده است؛ اصلاحی مجاز نیست و فقط ابطال امکانپذیر است. پیشنویس اصلاحی ثبتشده هم تا وقتی وضعیت «تأیید شده» است ارسال نمیشود. |
EXCHANGE_ANNOUNCEMENT_STATUS_UNAVAILABLE | $.data[i]، $.internalIds[i] یا $ | وضعیت تأیید اعلامیه موقتاً قابل خواندن نیست؛ اصلاحی الگوی ۱۱ فعلاً ممکن نیست. چند دقیقه بعد دوباره تلاش کنید. سایر صورتحسابها تحت تأثیر نیستند. |
INVOICE_NOT_FOUND | $.internalId یا $.taxId | صورتحسابی با این شناسه در شرکت احراز هویتشده یافت نشد. |
EXCHANGE_ANNOUNCEMENT_NOT_APPLICABLE | $ | صورتحساب اصلی زنجیره نوع اول با الگوی ۱۱ نیست، هنوز با موفقیت ارسال نشده، یا صورتحساب درخواستی ابطالی است. |
EXCHANGE_ANNOUNCEMENT_FORBIDDEN | $ | وضعیت از سامانه سازمان (source: orgApi) دریافت شده است و فقط مدیر سامانه میتواند آن را بهصورت دستی تغییر دهد. |
EXCHANGE_ANNOUNCEMENT_NOTE_REQUIRED | $.note | برای تغییر دستی وضعیتی که از سامانه سازمان دریافت شده، ثبت توضیح الزامی است. |
EXCHANGE_ANNOUNCEMENT_VERSION_CONFLICT | $.expectedVersion | وضعیت همزمان تغییر کرده است؛ دوباره استعلام کنید و با version جدید ارسال کنید. |
EXCHANGE_ANNOUNCEMENT_REQUEST_INVALID | $، $.confirmed، $.note یا $.expectedVersion | درخواست نامعتبر است: هیچیک یا هر دو فیلد internalId و taxId ارسال شده، confirmed ارسال نشده، توضیح بیش از ۵۰۰ کاراکتر است یا expectedVersion معتبر نیست. |
INVOICE_PAYMENT_NOT_APPLICABLE | $.internalId | روی این صورتحساب پرداخت در کارپوشه ثبت نمیشود: ارسالنشده یا بینتیجه، ابطالی، الگوی بورس، یا روش تسویه نقدی. کد دلیل در field (مانند CASH_SETTLEMENT، INVOICE_NOT_APPROVED) و توضیح در message. |
INVOICE_PAYMENT_DATE_BEFORE_ISSUE | $.payments[i].dateTime | تاریخ و زمان پرداخت قبل از تاریخ و زمان صدور صورتحساب است؛ سازمان چنین پرداختی را نمیپذیرد (کد رسمی 10014). |
INVOICE_PAYMENT_DATE_IN_FUTURE | $.payments[i].dateTime | تاریخ و زمان پرداخت بعد از زمان حال (به وقت تهران، با چند دقیقه اغماض) است؛ پرداختی که هنوز انجام نشده ثبت نمیشود (کد رسمی 10013). |
INVOICE_PAYMENT_ROW_LOCKED | $.payments | ردیفی که سازمان دارد تغییر کرده یا حذف شده است؛ فقط افزودن پرداخت جدید ممکن است. |
INVOICE_PAYMENT_DUPLICATE_REQUEST | $.uuid | uuid قبلاً برای این شرکت استفاده شده است (result: -2)؛ وضعیت را با inquiryPayment ببینید و با uuid تازه فقط پرداختهای ثبتنشده را بفرستید. |
INVOICE_PAYMENT_REGISTRATION_DISABLED | $ | ثبت پرداخت در کارپوشه موقتاً در سامانه غیرفعال است. |
INVOICE_PAYMENT_INTERNAL_ERROR | $ | خطای داخلی؛ با traceId به پشتیبانی مراجعه کنید. |
نمونه پاسخ جزئی: یک ثبت موفق و یک خطا
{
"result": 1,
"traceId": "d36131a5-11e0-402b-9b0b-4a921333c317",
"message": "1 صورتحساب ثبت شد و 1 مورد دارای خطا بود.",
"errors": [
{
"path": "$.data[1].data.Header.tbill",
"message": "مجموع صورتحساب باید با جمع مقادیر متناظر در Body برابر باشد. مقدار صحیح: 5500000"
}
],
"data": [
{
"asatspId": 105234,
"status": 0,
"issue": 1,
"internalId": "1000001"
},
{
"status": 3,
"error": [
"$.data[1].data.Header.tbill: مجموع صورتحساب باید با جمع مقادیر متناظر در Body برابر باشد. مقدار صحیح: 5500000"
],
"internalId": "1000002"
}
]
}
نمونه رد کامل بهدلیل عبور از سقف batch
{
"result": 0,
"traceId": "17f35c78-58b2-4f8f-8d5d-5e1ea281c1af",
"message": "ساختار کلی درخواست معتبر نیست. تعداد خطا: 1",
"errors": [
{
"path": "$.data",
"message": "آرایه data نمیتواند بیشتر از 250 صورتحساب داشته باشد."
}
]
}
نمونه پاسخ موفق ثبت صورتحساب
{
"result": 1,
"traceId": "5f1ca11c-56d0-4420-8f72-7d0ef41751f1",
"message": "صورتحسابها با موفقیت ثبت شدند.",
"data": [
{
"asatspId": 105234,
"status": 0,
"issue": 1,
"internalId": "1000001"
},
{
"asatspId": 105235,
"status": 0,
"issue": 1,
"internalId": "1000002"
}
]
}
پیگیری وضعیت صورتحساب
سرویسهای پیگیری وضعیت با Access Token دارای scope v2.invoice.read فراخوانی میشوند و آخرین وضعیت پردازش صورتحساب را بازمیگردانند. در دوره مهاجرت، Customer API Key قدیمی هم پذیرفته میشود.
برای هر سه روش پیگیری، مسیر تکمقداری و مسیر آرایهای مستقل وجود دارد. مسیرهای آرایهای حداکثر ۲۵۰ مقدار میپذیرند و پاسخ را دقیقاً با ترتیب ورودی برمیگردانند؛ بنابراین نتیجه یا خطای هر عضو با همان index ورودی متناظر است.
| مسیر | فیلد درخواست | کاربرد |
|---|---|---|
| POST /api/invoice/inquiryInternalId | internalId | پیگیری با کد داخلی ارسالی. |
| POST /api/invoice/inquiryInternalIds | internalIds[] | پیگیری گروهی با کدهای داخلی؛ حداکثر ۲۵۰ مقدار. |
| POST /api/invoice/inquiryNo | no | پیگیری با شماره صورتحساب یا inno. |
| POST /api/invoice/inquiryNos | nos[] | پیگیری گروهی با شمارههای صورتحساب یا inno؛ حداکثر ۲۵۰ مقدار. |
| POST /api/invoice/inquiryTaxId | taxId | پیگیری با شماره منحصر به فرد مالیاتی. |
| POST /api/invoice/inquiryTaxIds | taxIds[] | پیگیری گروهی با شمارههای منحصر به فرد مالیاتی؛ حداکثر ۲۵۰ مقدار. |
{
"internalId": "1000001"
}
{
"internalIds": [
"1000001",
"1000002"
]
}
{
"no": "A000000001"
}
{
"nos": [
"A000000001",
"A000000002"
]
}
{
"taxId": "A1B2C3D4E5F6G7H8I9J0K1"
}
{
"taxIds": [
"A1B2C304E7900000F42410",
"SND12304E7900000F42411"
]
}
curl -X POST "https://api-v2.asatsp.ir/api/invoice/inquiryInternalIds" \
-H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
-H "Content-Type: application/json; charset=utf-8" \
-d '{"internalIds":["1000001","1000002"]}'
نمونه پاسخ آرایهای
در پاسخ گروهی، شناسه جستوجوشده روی همان عضو بازگردانده میشود. یافتنشدن یک صورتحساب مانع پیگیری سایر اعضا نیست.
[
{
"asatspId": 105234,
"taxId": "A1B2C304E7900000F42410",
"status": 2,
"internalId": "1000001"
},
{
"status": 3,
"error": ["صورتحساب یافت نشد"],
"internalId": "1000002"
}
]
وضعیتهای رایج
| status | معنا | اقدام پیشنهادی |
|---|---|---|
0 | در صف ارسال | پس از چند دقیقه، وضعیت را دوباره پیگیری کنید. |
1 | ارسالشده و در انتظار پاسخ سامانه | پس از چند دقیقه، وضعیت را دوباره پیگیری کنید. |
2 | موفق | شماره مرجع و taxId را ذخیره کنید. |
3 | دارای خطا | پیام خطا را در error بررسی کنید؛ در این وضعیت json برنمیگردد. |
refrenceNumber (شماره مرجع سامانه مؤدیان) فقط در وضعیتهای 1 و 2 برمیگردد (در وضعیت 1 ممکن است هنوز خالی باشد) و در وضعیتهای 0 و 3 نمیآید. فقط در وضعیت 2 (موفق)، json (همان payload ارسالشده به سامانه مؤدیان) و در صورت وجود مقدار، statusInquery (وضعیت صورتحساب/واکنش خریدار در سامانه مؤدیان) و article6Status (وضعیت ماده ۶: عدول یا عدم عدول) هم برمیگردند. پاسخ پیگیری فیلد جداگانهای برای ردیفها ندارد. در الگوی ۱۱، در json.Body[0] adis و am مقدار دارند ولی fee، prdis و dis مقداری ندارند (برابر null هستند یا اصلاً نمیآیند)؛ مبلغ واحد نمایشی ذخیرهشده در پاسخ پیگیری برنمیگردد (مبلغ واحد در الگوی ۱۱).
وضعیت تأیید اعلامیه فروش بورس (الگوی ۱۱)
طبق دستورالعمل RC_IITP.IS_V7.9، برای صورتحساب نوع اول با الگوی بورس اوراق بهادار مبتنی بر کالا (inp=11)، صدور اصلاحی (ins=2) فقط تا زمانی مجاز است که اعلامیه فروش بورس تأیید نشده باشد؛ پس از تأیید فقط ابطال (ins=3) مجاز است. سامانه مؤدیان راهی برای استعلام این تأیید ندارد، بنابراین وضعیت هر زنجیره صورتحساب بهصورت دستی (از پنل یا همین سرویسها) ثبت میشود و همه تصمیمهای اصلاحی آن زنجیره در پنل و API از همین وضعیت پیروی میکنند.
| مسیر | scope | بدنه | کاربرد |
|---|---|---|---|
| POST /api/invoice/inquiryExchangeAnnouncement | v2.invoice.read | internalId یا taxId | استعلام وضعیت فعلی زنجیره. |
| POST /api/invoice/exchangeAnnouncement | v2.invoice.send | internalId یا taxId، confirmed، note، expectedVersion | ثبت دستی «تأیید شده» یا «تأیید نشده». |
هر internalId یا taxId از زنجیره (اصلی یا اصلاحی) را میتوانید بفرستید؛ وضعیت یک بار برای کل زنجیره ذخیره میشود و کلید آن originTaxId (شماره مالیاتی صورتحساب اصلی زنجیره) است. صورتحساب فقط در محدوده شرکت احراز هویتشده جستوجو میشود.
صورتحساب اصلی زنجیره باید نوع اول، الگوی ۱۱ و با موفقیت ارسالشده باشد. برای صورتحساب ابطالی وضعیت ثبت نمیشود. در غیر این صورت خطای EXCHANGE_ANNOUNCEMENT_NOT_APPLICABLE برمیگردد.
ارسال دوباره همان وضعیت (و همان note یا بدون note) چیزی ثبت نمیکند و پاسخ موفق با وضعیت فعلی برمیگرداند؛ بنابراین تلاش مجدد پس از قطع ارتباط امن است.
expectedVersion اختیاری است. اگر مقدار version آخرین استعلام را بفرستید و وضعیت در این فاصله تغییر کرده باشد، خطای EXCHANGE_ANNOUNCEMENT_VERSION_CONFLICT برمیگردد.
فیلدهای درخواست
| فیلد | قاعده | توضیح |
|---|---|---|
token | اختیاری | توکن تکمیلی نمایندگی، مانند سایر سرویسها. |
internalId | دقیقاً یکی از internalId یا taxId | کد داخلی صورتحساب. |
taxId | دقیقاً یکی از internalId یا taxId | شماره منحصر به فرد مالیاتی صورتحساب. |
confirmed | فقط در exchangeAnnouncement، الزامی، true یا false | true یعنی اعلامیه فروش بورس تأیید شده و false یعنی تأیید نشده است. |
note | اختیاری، حداکثر ۵۰۰ کاراکتر | توضیح تغییر؛ در تاریخچه ثبت میشود. |
expectedVersion | اختیاری | همان version پاسخ استعلام. |
مقادیر وضعیت
| status | معنا | اثر بر اصلاحی |
|---|---|---|
notRecorded | هنوز وضعیتی ثبت نشده است. | اصلاحی مانند گذشته مجاز است. |
notConfirmed | اعلامیه فروش بورس تأیید نشده است. | اصلاحی مجاز است. |
confirmed | اعلامیه فروش بورس تأیید شده است. | فقط ابطال؛ اصلاحی با EXCHANGE_ANNOUNCEMENT_CONFIRMED رد میشود. |
منبع و کانال ثبت
| فیلد | مقدار | معنا |
|---|---|---|
source | manual | ثبت دستی (پنل، API نسخه ۲ یا اسکریپت پشتیبانی). |
source | orgApi | دریافتشده از API آتی سازمان. تغییر دستی آن فقط برای مدیر سامانه و با توضیح مجاز است؛ در API نسخه ۲ خطای EXCHANGE_ANNOUNCEMENT_FORBIDDEN برمیگردد. |
channel | panel، apiV2، orgApi، script | مسیری که آخرین ثبت از آن انجام شده است. ثبت از این سرویس همیشه apiV2 است و setBy شناسه client یا legacy-company:{companyId} را نشان میدهد. |
فیلدهای پاسخ
پاسخ در قالب همیشگی (result، traceId، message، errors، data) با HTTP 200 برمیگردد و وضعیت در فیلد exchangeAnnouncement قرار دارد. در خطا، result برابر 0 (و در خطای احراز هویت -1) است و کد خطا در errors[].code میآید؛ فهرست کدها در جدول خطاها.
| فیلد | توضیح |
|---|---|
invoiceId | شناسه صورتحساب درخواستی. |
originTaxId، rootInvoiceId | شماره مالیاتی و شناسه صورتحساب اصلی زنجیره (کلید وضعیت). |
status | notRecorded، notConfirmed یا confirmed. |
source، channel | منبع و کانال آخرین ثبت؛ پیش از اولین ثبت وجود ندارند. |
setBy، setByUserId، setAtUtc، setAt | ثبتکننده و زمان آخرین ثبت (setAt به تاریخ شمسی و وقت تهران). |
saleAnnNo، saleAnnDate | شماره و تاریخ شمسی اعلامیه فروش بورس زنجیره. |
note | توضیح آخرین ثبت. |
version | نسخه ردیف وضعیت؛ برای expectedVersion. |
canChange | آیا این توکن میتواند وضعیت را تغییر دهد (نیازمند v2.invoice.send و منبع غیر orgApi). |
pendingCorrectionDrafts | اصلاحیهای باز زنجیره (invoiceId، no، hash): پیشنویسهای ارسالنشده، و اصلاحیهایی که برای ارسال ثبت شدهاند ولی هنوز نتیجهای از سامانه مؤدیان ندارند با inFlight: true. برای پیشنویس ارسالنشده فیلد inFlight وجود ندارد. |
pendingCorrectionDrafts فهرست میشوند. تا وقتی وضعیت «تأیید شده» است، این پیشنویسها نه ذخیره میشوند و نه ارسال؛ برای مثال /api/invoice/sendInvoice برای آنها خطای EXCHANGE_ANNOUNCEMENT_CONFIRMED در مسیر $.internalIds[i] برمیگرداند. اصلاحیای که پیش از ثبت «تأیید شده» برای ارسال ثبت شده ولی هنوز نتیجهای ندارد با inFlight: true در همین فهرست میآید؛ تا وقتی وضعیت «تأیید شده» است، سامانه آن را دوباره در صف ارسال قرار نمیدهد.
sendInvoice و cancelInvoice همیشه روی آخرین صورتحساب زنجیره عمل میکنند. بنابراین:
sendInvoiceباinternalIdصورتحساب اصلی یا هر عضو قبلی زنجیره، همان پیشنویس اصلاحی را ارسال میکند و در وضعیت «تأیید شده» باEXCHANGE_ANNOUNCEMENT_CONFIRMEDدر مسیر$.internalIds[i]رد میشود. چون با هر خطا پاسخresult: 0بدونdataبرمیگردد، سایر شناسههای همان درخواست را که ممکن است در صف ارسال قرار گرفته باشند با سرویسهای پیگیری بررسی کنید.cancelInvoiceفقط آخرین صورتحساب زنجیره را، در صورتی که ارسالشده و دارای شماره مالیاتی باشد، باطل میکند؛ تا وقتی پیشنویس اصلاحی ارسالنشده آخرین صورتحساب زنجیره است، ابطال با خطا در مسیر$.internalIdرد میشود.- API نسخه ۲ امکان حذف پیشنویس ارسالنشده را ندارد. برای ابطال زنجیره، ابتدا پیشنویس را در پنل حذف کنید تا صورتحساب قبلی دوباره آخرین صورتحساب زنجیره شود، سپس
cancelInvoiceرا فراخوانی کنید. - اگر «تأیید شده» به اشتباه ثبت شده است، ثبت
confirmed: falseاصلاحیها و پیشنویسهای همان زنجیره را دوباره قابل ثبت و ارسال میکند. این تغییر برای وضعیت دریافتشده از سامانه سازمان (source: orgApi) باEXCHANGE_ANNOUNCEMENT_FORBIDDENرد میشود.
EXCHANGE_ANNOUNCEMENT_STATUS_UNAVAILABLE متوقف میشوند؛ صورتحساب اصلی، ابطال، برگشت از فروش و صورتحسابهای سایر الگوها تحت تأثیر نیستند.
inquiryInternalId، inquiryNo، inquiryTaxId و نسخههای آرایهای)، پاسخ ثبت و پاسخ sendInvoice و cancelInvoice، برای صورتحساب الگوی ۱۱ فیلد اختیاری exchangeAnnouncement با ساختار کوتاه { status, source?, setBy?, setAt?, setAtUtc? } اضافه میشود. برای سایر الگوها، یا اگر وضعیت قابل خواندن نباشد، این فیلد در پاسخ وجود ندارد.
{
"taxId": "A1B2C304E7900000F42410"
}
{
"internalId": "IME-1001",
"confirmed": true,
"note": "اعلامیه در سامانه بورس کالا تأیید شد.",
"expectedVersion": "AAAAAAAAB9A="
}
{
"result": 1,
"traceId": "0c5d2a4e-6f1b-4c3e-9a57-2b8f4d1e7a90",
"message": "وضعیت اعلامیه فروش بورس ثبت شد.",
"data": [],
"exchangeAnnouncement": {
"invoiceId": 105234,
"originTaxId": "A1B2C304E7900000F42410",
"rootInvoiceId": 105234,
"status": "confirmed",
"source": "manual",
"channel": "apiV2",
"setBy": "API v2 (customer:81001)",
"setAtUtc": "2026-09-15T06:30:00Z",
"setAt": "1405/06/24 ساعت 10:00",
"saleAnnNo": "1405012345",
"saleAnnDate": "1405/06/10",
"note": "اعلامیه در سامانه بورس کالا تأیید شد.",
"version": "AAAAAAAAB9E=",
"canChange": true,
"pendingCorrectionDrafts": [
{ "invoiceId": 105240, "no": "A000000012", "hash": "9b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e" },
{ "invoiceId": 105241, "no": "A000000013", "hash": "0a1b2c3d4e5f60718293a4b5c6d7e8f9", "inFlight": true }
]
}
}
ثبت پرداخت صورتحساب ارسالشده در کارپوشه
برای صورتحسابی که با روش تسویه نسیه یا نقدی/نسیه به سامانه مؤدیان ارسال و در کارپوشه ثبت شده است، پرداختهای بعدی خریدار از طریق سرویس رسمی «ثبت پرداخت صورتحساب» (invoice-payment، مستند RC_TICS.IS) در کارپوشه ثبت میشوند. این سرویس همان کار را برای شما انجام میدهد: پرداختهای جدید روی آخرین صورتحساب زنجیره internalId ذخیره میشوند، نسخهٔ جدید صورتحساب با همان شماره مالیاتی ساخته میشود (اصلاحی صادر نمیشود) و هر ردیف جدید در همان درخواست به سامانه مؤدیان فرستاده میشود. اگر پاسخ سازمان به موقع نرسد یا خطای گذرا (مانند کد 10015) برگردد، ثبت بهصورت خودکار دوباره تلاش میشود و نتیجه را با /api/invoice/inquiryPayment پیگیری میکنید.
| مسیر | scope | بدنه | کاربرد |
|---|---|---|---|
| POST /api/invoice/registerPayment | v2.invoice.send | uuid، internalId، payments[] | افزودن پرداختهای جدید به صورتحساب ارسالشده و ثبت آنها در کارپوشه. |
| POST /api/invoice/inquiryPayment | v2.invoice.read | internalId | وضعیت همهٔ ردیفهای پرداخت صورتحساب و وضعیت ثبت هر کدام در کارپوشه. |
آخرین صورتحساب زنجیره باید ارسالشده و در سامانه مؤدیان ثبت شده باشد (وضعیت تأیید شده، تأیید سیستمی یا در انتظار واکنش)، ابطالی نباشد و روش تسویهای که به سازمان ارسال شده نقدی نباشد؛ الگوی بورس (inp=11) اطلاعات پرداخت ندارد. روش تسویهٔ صورتحساب با ثبت پرداخت تغییر نمیکند (همان است که به سازمان رفته). در غیر این صورت خطای INVOICE_PAYMENT_NOT_APPLICABLE با دلیل در errors[].message و کد دلیل در errors[].field برمیگردد و چیزی ذخیره نمیشود.
تاریخ و زمان هر پرداخت نباید قبل از تاریخ و زمان صدور صورتحساب باشد (خطای INVOICE_PAYMENT_DATE_BEFORE_ISSUE در مسیر $.payments[i].dateTime) و نباید بعد از زمان حال باشد (خطای INVOICE_PAYMENT_DATE_IN_FUTURE در همان مسیر، معادل کد رسمی 10013؛ مقایسه به وقت تهران و با چند دقیقه اغماض برای اختلاف ساعت). سازمان همچنین پرداخت خارج از دورهٔ جاری را نمیپذیرد (کد رسمی 10012)؛ این یکی در نتیجهٔ ثبت همان ردیف با وضعیت rejected برمیگردد.
ردیفی که سازمان دارد (همراه صورتحساب رفته یا با این سرویس ثبت شده) دیگر قابل ویرایش یا حذف نیست، چون سامانه مؤدیان سرویسی برای حذف یا اصلاح پرداخت ندارد. این سرویس فقط پرداخت اضافه میکند؛ در پاسخ، payments[].locked این ردیفها را نشان میدهد.
uuid برای هر درخواست الزامی و در محدودهٔ شرکت یکتاست؛ ارسال دوبارهٔ همان uuid با result: -2 و کد INVOICE_PAYMENT_DUPLICATE_REQUEST رد میشود تا یک پرداخت دو بار ثبت نشود. پس از قطع ارتباط، ابتدا با inquiryPayment وضعیت را ببینید و فقط پرداختهایی را که در فهرست نیستند با uuid تازه بفرستید.
فیلدهای درخواست registerPayment
| فیلد | قاعده | توضیح |
|---|---|---|
token | اختیاری | توکن تکمیلی نمایندگی، مانند سایر سرویسها. |
uuid | الزامی، GUID یکتا برای شرکت | شناسهٔ این درخواست؛ برای کنترل تکراریبودن. |
internalId | الزامی | کد داخلی صورتحساب؛ آخرین صورتحساب زنجیرهٔ آن در نظر گرفته میشود. |
payments | آرایهٔ ۱ تا ۵۰ عضو | هر عضو با همان مدل payments[] مدل ساده (بخش ۰۶). |
payments[].type | الزامی، 1 تا 8 | همان کدهای روش پرداخت مدل ساده (جدول مقادیر شمارشی)؛ به ترتیب به paymentMethod سرویس سازمان (CHEQUE، BARTER، CASH، POS، INTERNET، CARD، TRANSFER، OTHER) نگاشت میشود. |
payments[].dateTime | الزامی؛ Unix time میلیثانیه، yyyyMMddHHmmss یا تاریخ شمسی 1405/06/25 10:30 | تاریخ و زمان پرداخت؛ نباید قبل از صدور صورتحساب باشد. |
payments[].price | الزامی، عدد ریالی بزرگتر از صفر، حداکثر ۱۸ رقم | مبلغ پرداخت همانطور که خریدار پرداخته (با مالیات بر ارزش افزوده)؛ اعشار حذف میشود. آنچه به کارپوشه میرود (paidAmount) سهم بدون مالیات همین مبلغ است: کارپوشه مبالغ تسویه را بدون مالیات نگه میدارد و «سهم مالیات بر ارزش افزوده از پرداخت» را خودش میافزاید؛ سهم مالیات با نسبت مالیات به مبلغ نهایی همان صورتحساب محاسبه و از مبلغ کم میشود (مثلاً روی صورتحساب ۱۰٪، پرداخت ۳٬۲۴۲٬۱۷۰ ریال با paidAmount برابر ۲٬۹۴۷٬۴۲۸ ثبت میشود). مبلغ ثبتشده در payments[].registration.registeredAmount برمیگردد. |
payments[].terminalNumber | اختیاری، فقط رقم، حداکثر ۸ رقم | شماره پایانه (terminalNumber). |
payments[].traceNo | اختیاری، فقط رقم، حداکثر ۱۴ رقم | شماره پیگیری یا مرجع پرداخت (referenceNumber). |
payments[].cardNo، payerNationalCode، switchNumber، acquirerNumber، description | اختیاری (رقم، به ترتیب حداکثر ۱۶، ۱۲، ۹ و ۱۴ رقم) | در سوابق شما ذخیره میشوند و به سرویس ثبت پرداخت سازمان فرستاده نمیشوند. |
{
"uuid": "1f0c0f1e-6b1c-4c7b-9a2e-4d5a9d2f7a11",
"internalId": "INV-10001",
"payments": [
{
"type": 6,
"dateTime": "1405/06/25 10:30",
"price": 25000000,
"traceNo": "13023878656431",
"terminalNumber": "1302387",
"description": "قسط دوم"
}
]
}
فیلدهای پاسخ
پاسخ در قالب همیشگی (result، traceId، message، errors، data) با HTTP 200 برمیگردد. عضو data[0] همان اطلاعات استعلام صورتحساب است بهعلاوهٔ paymentRegistration (قابلیت ثبت پرداخت روی این صورتحساب) و payments[] (همهٔ ردیفهای پرداخت صورتحساب با وضعیت هر کدام). در خطا، result برابر 0 (تکراری -2، احراز هویت -1) است و کدها در errors[].code میآیند.
| فیلد | توضیح |
|---|---|
paymentRegistration.eligible | true یعنی پرداختهای جدید روی این صورتحساب در کارپوشه ثبت میشوند؛ در غیر این صورت code و reason دلیل را میگویند. |
paymentRegistration.rowsLocked | صورتحساب ارسالشده است و ردیفهای قبلی پرداخت آن قابل تغییر نیستند. |
payments[].id، type، dateTime، timestamp، price، traceNo، terminalNumber | ردیف پرداخت؛ dateTime شمسی و timestamp همان زمان به Unix time میلیثانیه. |
payments[].locked | ردیف نزد سازمان است (یا ممکن است باشد) و دیگر تغییر نمیکند. |
payments[].registration.status | وضعیت ثبت در کارپوشه؛ مقادیر در جدول زیر. |
payments[].registration.code، message | کد و پیام رسمی سازمان در رد شدن (مثلاً 10014)، یا کد داخلی خطای انتقال. |
payments[].registration.registeredAmount | مبلغی که برای این ردیف به کارپوشه فرستاده شده (paidAmount، بدون سهم مالیات بر ارزش افزوده)؛ فقط وقتی ثبتی وجود دارد. |
payments[].registration.registeredAt، lastAttemptAt، taxCreateDate | زمان ثبت موفق، زمان آخرین تلاش و createDate برگشتی از سازمان (Unix time میلیثانیه). |
مقادیر status
| status | معنا | اقدام |
|---|---|---|
registered | سازمان ثبت پرداخت را با SUCCESS پذیرفته است. | — |
queued | در صف ثبت یا تلاش مجدد خودکار (پاسخ سازمان به موقع نرسیده یا خطای گذرا داشته). | بعداً با inquiryPayment پیگیری کنید. |
rejected | سازمان با خطای رسمی رد کرده است (code و message). | علت را رفع کنید؛ ردیف ردشده در پنل قابل ویرایش است. |
unknown | درخواست به سازمان رسیده ولی پاسخ آن گم شده است؛ ممکن است ثبت شده باشد. | خودکار تکرار نمیشود؛ ابتدا کارپوشه را بررسی کنید و در صورت نیاز از پنل «تلاش مجدد» بزنید. |
notApplicable | هرگز فرستاده نشده است (تسویه نقدی، صورتحساب ابطالی، روش یا تاریخ نامعتبر). | — |
failed | تلاشهای خودکار به پایان رسیده است. | از پنل «تلاش مجدد» بزنید یا با پشتیبانی تماس بگیرید. |
sentWithInvoice | ردیف همراه خود صورتحساب به سازمان رفته است (بخش Payments صورتحساب). | — |
notRegistered | صورتحساب هنوز ارسال نشده و ردیف با خود صورتحساب خواهد رفت. | — |
{
"result": 1,
"traceId": "0f6c5f7a-2f6b-4a4f-9f5a-1b2c3d4e5f60",
"message": "پرداختها ثبت شدند.",
"data": [
{
"internalId": "INV-10001",
"taxId": "A111H104EA6001D0B32AC6",
"status": 3,
"statusInquery": "AWAITING_REACTION",
"paymentRegistration": { "eligible": true, "rowsLocked": true },
"payments": [
{
"id": 50622,
"type": 6,
"dateTime": "1405/06/25 10:30",
"timestamp": 1789446000000,
"price": 25000000,
"traceNo": "13023878656431",
"terminalNumber": "1302387",
"description": "قسط دوم",
"locked": true,
"registration": {
"status": "registered",
"registeredAt": 1789446065000,
"taxCreateDate": 1789446064000,
"registrationId": 12,
"registeredAmount": 22727273
}
}
]
}
]
}
payments[] (مدل ساده) یا Payments (JSON سازمان) میفرستید همراه خود صورتحساب به سازمان میروند و در استعلام با وضعیت sentWithInvoice دیده میشوند.
کارپوشه مالیاتی، واکنش گروهی و فایل Excel
این API همان منطق مرکزی کارپوشه در پنل را اجرا میکند. کسبوکار، کد اقتصادی، Profile و Delegation فقط از Access Token و Context احرازشده سرور تعیین میشوند؛ هیچیک از مقادیر داخل JSON یا Excel نمیتوانند Tenant یا مؤدی را انتخاب یا تغییر دهند.
client_credentials نوع Customer استفاده میکنند. عملیات صرفاً خواندنی Export، Template و Result به scope v2.invoice.read نیاز دارند. Preview هیچ PUT سازمانی اجرا نمیکند، اما چون Actionability و Permission هر ردیف را با همان Preflight عملیات بررسی میکند، مانند Bulk/Execute به scope v2.invoice.send نیاز دارد. نبود scope با HTTP 403 برگردانده میشود. API Key قدیمی فقط تا مهلت مهاجرت اعلامشده و با همان هدرهای Deprecation قبلی پشتیبانی میشود.
contract.brokerage:view وابسته است. تأیید یا رد قرارداد علاوه بر scope داخلی، به Permission واگذارشده contract.brokerage:manage و روشن بودن TaxWorkspace.ContractReactionsEnabled نیاز دارد. Tokenهای قبلی فاقد این Permission برای قابلیتهای دیگر Invalid نمیشوند.
TaxWorkspace.Enabled=false تحویل میشود. پیش از فعالسازی Production باید Environment، URLهای رسمی، Client ID، مسیرهای Certificate/Private Key و Encryption Secret از Environment/Secret Store همان App Pool تنظیم و دسترسی خواندن کنترل شود؛ مقدار محرمانه نباید در Web.config قرار گیرد. چون API v2 همان Profile، Delegation و داده رمزشده MegaPayBackend را استفاده میکند، مقدار TRUSTED_COMPANY_SERVICES_ENCRYPTION_KEY، Environment و Trusted Company Client ID باید دقیقاً با Host اصلی MegaPayBackend یکسان باشند و endpoint/certificate identity نیز همسان تنظیم شود؛ برای این Host کلید جداگانه تولید نکنید.
Endpointها
| Method | Endpoint | Scope | کاربرد |
|---|---|---|---|
PUT | /api/v2/tax-workspace/invoices/bulk | v2.invoice.send | Approve/Reject گروهی صورتحساب خرید، خرید مجازی و فروش مجازی با JSON. |
PUT | /api/v2/tax-workspace/brokerage-contracts/bulk | v2.invoice.send | Approve/Reject گروهی قرارداد شخص ثالث؛ هر قرارداد با Request مستقل سازمان اجرا میشود. |
GET | /api/v2/tax-workspace/invoices/{kind}/excel/export | v2.invoice.read | خروجی همه نتایج منطبق با Filter و Sort، نه فقط صفحه جاری. |
GET | /api/v2/tax-workspace/invoices/{kind}/excel/template | v2.invoice.read | نمونه امن .xlsx برای بخشهای دارای واکنش. |
POST | /api/v2/tax-workspace/invoices/{kind}/excel/preview | v2.invoice.send | آپلود multipart، Parse و Preflight خواندنی Actionability/Permission؛ هیچ PUT سازمانی اجرا نمیشود. |
POST | /api/v2/tax-workspace/invoices/{kind}/excel/execute | v2.invoice.send | اجرای Preview تأییدشده با previewToken کوتاهعمر. |
GET | /api/v2/tax-workspace/brokerage-contracts/excel/export | v2.invoice.read | خروجی Filterشده قراردادها. |
GET | /api/v2/tax-workspace/brokerage-contracts/excel/template | v2.invoice.read | نمونه عملیات قرارداد. |
POST | /api/v2/tax-workspace/brokerage-contracts/excel/preview | v2.invoice.send | اعتبارسنجی و Preflight قرارداد بدون اجرای PUT. |
POST | /api/v2/tax-workspace/brokerage-contracts/excel/execute | v2.invoice.send | اجرای عملیات قرارداد پس از Confirmation. |
POST | /api/v2/tax-workspace/excel/result | v2.invoice.read | دریافت فایل نتیجه با resultToken متعلق به همان Company/API Client. |
Kind و Filter خروجی صورتحساب
مقادیر مجاز {kind} عبارتاند از purchase، sales، virtual-purchase و virtual-sales. فروش عادی فقط Export دارد و endpoint واکنش ساختگی برای آن ایجاد نشده است. خرید مجازی از همان سرویس رسمی واکنش خرید استفاده میکند.
| Filter | توضیح |
|---|---|
period | دوره مالیاتی با قالب مورد قبول سرویس سازمان. |
status و pattern | وضعیت و الگوی صورتحساب. |
issuanceDateFrom / issuanceDateTo | بازه تاریخ صدور با epoch milliseconds. |
contractBrokerageNumber | شماره قرارداد شخص ثالث مرتبط. |
sellerRole | فقط در فهرست فروش کارپوشه. |
Export قرارداد نیز Filterهای contractBrokerageId، contractBrokerageNumber، contractBrokerageInternalNumber، status، کدهای اقتصادی طرفین و بازههای issue/create date را میپذیرد. سرور صفحات حداکثر ۱۰تایی سازمان را کنترلشده واکشی میکند و از سقف TaxWorkspace.ExcelExportMaxRows عبور نمیکند.
JSON Bulk صورتحساب
در حالت ساده فقط kind، آرایه taxIds و action ارسال میشود. Backend روز صدور و دوره مالیاتی را از بخش روز رسمی TaxId استخراج میکند، حداکثر دو تاریخ محتمل تهران را در فهرست واگذارشده همان کارپوشه استعلام میگیرد و فقط TaxId دقیق با وضعیت AWAITING_REACTION را قبل از PUT میپذیرد. بنابراین Client برای تأیید نیازی به ارسال period یا تاریخ ندارد و TaxId نیز نمیتواند کارپوشه دیگری را انتخاب کند.
{
"kind": "purchase",
"taxIds": ["<TaxId1>", "<TaxId2>"],
"action": "APPROVE"
}
{
"kind": "purchase",
"taxIds": ["<TaxId1>", "<TaxId2>"],
"action": "REJECT",
"rejectReasons": ["PRODUCT", "AMOUNT"],
"rejectDescription": "مغایرت اطلاعات ردیفها"
}
Action فقط APPROVE یا REJECT است. دلیلهای مجاز: NOT_INFORMED، BUYER، SELLER، PRODUCT، AMOUNT، PAYMENT و OTHER. برای OTHER توضیح الزامی است. برای Actionهای متفاوت در یک درخواست، شکل تفصیلی سازگار با نسخه قبل یعنی operations[] همچنان پشتیبانی میشود؛ در این شکل period و issuanceDate اختیاریاند و در صورت حذف از TaxId استخراج میشوند. ارسال همزمان taxIds و operations با invalid_bulk_shape رد میشود.
{
"kind": "purchase",
"operations": [
{
"taxId": "A111111111111111111111",
"period": "140501",
"issuanceDate": 1780000000000,
"action": "APPROVE",
"rejectReasons": [],
"rejectDescription": null
},
{
"taxId": "A222222222222222222222",
"period": "140501",
"issuanceDate": 1780000000000,
"action": "REJECT",
"rejectReasons": ["PRODUCT", "AMOUNT"],
"rejectDescription": "مغایرت ردیف نمونه"
}
]
}
{
"status": "OK",
"data": {
"total": 2,
"success": 1,
"failed": 1,
"skipped": 0,
"results": [
{
"identifier": "A111111111111111111111",
"period": "140501",
"issuanceDate": 1780000000000,
"action": "APPROVE",
"status": "SUCCESS",
"code": null,
"message": "عملیات انجام شد.",
"correlationId": "00000000-0000-0000-0000-000000000001"
},
{
"identifier": "A222222222222222222222",
"period": "140501",
"issuanceDate": 1780000000000,
"action": "REJECT",
"status": "FAILED",
"code": "invoice_not_actionable",
"message": "صورتحساب در وضعیت قابل واکنش نیست.",
"correlationId": "00000000-0000-0000-0000-000000000001"
}
]
},
"errors": [],
"fieldErrors": [],
"correlationId": "00000000-0000-0000-0000-000000000001"
}
JSON Bulk قرارداد
{
"contractBrokerageNumbers": ["000000000001", "000000000002"],
"action": "APPROVE"
}
{
"allForCurrentEconomicNumber": true,
"economicNumber": "<CurrentWorkspaceEconomicNumber>",
"action": "APPROVE"
}
برای انتخاب همه، فقط قراردادهای AWAITING_REACTION و قابل اقدام در کارپوشه احرازشده فعلی انتخاب میشوند. economicNumber اختیاری و صرفاً تأیید تطابق است؛ Backend هرگز از آن برای انتخاب Tenant یا Delegation استفاده نمیکند و مقدار متفاوت را با economic_number_context_mismatch رد میکند. شکل قبلی operations[] با contractBrokerageId یا contractBrokerageNumber همچنان برای Actionهای ترکیبی معتبر است، ولی سه روش انتخاب را نمیتوان همزمان فرستاد. endpoint رسمی قرارداد یک شناسه میپذیرد؛ بنابراین API هر Contract را با Request مستقل و concurrency محدود اجرا میکند و batch ساختگی برای سازمان نمیسازد. تعداد Operationهای واقعاً قابل اجرا در هر درخواست JSON یا فایل Excel با TaxWorkspace.ContractBulkMaxActions محدود میشود؛ مقدار پیشفرض امن 20 و بازه قابل تنظیم 1..100 است. ردیفهای بدون Action یا نامعتبر در این سقف اجرایی شمرده نمیشوند.
Chunking، Atomicity و Partial Success
تعداد Operationهای واقعاً قابل اجرای صورتحساب در یک درخواست JSON یا فایل Excel با TaxWorkspace.InvoiceBulkMaxActions محدود میشود؛ مقدار پیشفرض 20 و بازه قابل تنظیم 1..100 است. این سقف با chunk حداکثر ۱۰تایی هر Request سازمان تفاوت دارد.
- Approve صورتحسابها در chunkهای حداکثر ۱۰تایی ارسال میشود.
- Rejectها ابتدا بر اساس ترکیب دقیق Reasons و Description گروهبندی و سپس حداکثر ۱۰تایی میشوند.
- Atomicity در سطح هر Request سازمان حفظ میشود؛ Fail شدن یک chunk همه اعضای همان chunk را Fail گزارش میکند.
- فایل بزرگ میتواند Partial Success داشته باشد؛ نتیجه هر ردیف در response و فایل نتیجه مشخص است.
- برای PUT هیچ Retry کور انجام نمیشود. نتیجه مبهم با GET Recheck فعلی بررسی و در صورت عدم قطعیت با
AMBIGUOUSگزارش میشود.
این endpointها بهصورت synchronous و با سقف اجرایی مشخص کار میکنند. Host فقط برای مسیر /api/v2/tax-workspace دارای executionTimeout=7200 ثانیه است تا عملیات ترتیبی قرارداد قبل از تکمیل توسط timeout پیشفرض ASP.NET قطع نشود. Client باید timeout متناسب با تعداد عملیات تنظیم کند؛ در صورت timeout یا پاسخ مبهم، PUT را کورکورانه تکرار نکند و ابتدا وضعیتها و نتیجه ثبتشده را بررسی کند.
Excel: Export، Preview، Confirmation و Result
- فایل واقعی Filterشده یا Template را دانلود کنید.
- نمونه صورتحساب فقط چهار ستون فارسی «شناسه یکتای مالیاتی»، «عملیات»، «دلایل رد» و «توضیحات رد» دارد. نمونه قرارداد فقط «شماره قرارداد» و «عملیات» دارد. چند دلیل فارسی با
;جدا میشوند؛ مانند «کالا یا خدمت;مبلغ صورتحساب». - برای واکنش به همه قراردادهای قابل اقدام همین کارپوشه، در ستون «شماره قرارداد» مقدار «همه» را بنویسید. فایل هیچ کد اقتصادی یا شناسه Tenant دریافت نمیکند.
- فایل را با
multipart/form-dataو نام فیلدfileبرای Preview ارسال کنید. Preview هیچ واکنشی اجرا نمیکند. - Summary شامل قابل اجرا، Approve، Reject، بدون Action و خطادار را بررسی کنید.
previewTokenکوتاهعمر را با endpoint Execute ارسال کنید.- پس از اجرا، فایل نتیجه را با
resultTokenدریافت کنید.
Workbook جدید بهصورت راستبهچپ و با Sheetهای فارسی دادهها، راهنما و فهرستها تولید میشود. عنوان ستونها، وضعیتها، نوعها، نقشها، عملیات «تأیید/رد» و دلیلهای رد با اصطلاحات فارسی مستندات نمایش داده میشوند و Backend آنها را پیش از اجرا به کدهای فنی ثابت تبدیل میکند. فایلهای قدیمی دارای Sheet Data و عنوانها یا کدهای انگلیسی همچنان قابل بارگذاریاند. فایل نتیجه ستونهای فارسی «نتیجه عملیات»، «زمان پردازش»، «کد پاسخ»، «پیام پاسخ» و «شناسه رهگیری» را اضافه میکند. هیچ Token، Secret یا Authorization Header در فایل نوشته نمیشود. Claim اجرای Excel با تنظیم TaxWorkspace.ExcelExecutionRetentionHours در بازه 3..168 ساعت نگهداری میشود و مقدار پیشفرض آن 24 ساعت است.
{
"status": "OK",
"data": {
"previewToken": "<PreviewToken>",
"batchCorrelationId": "00000000-0000-0000-0000-000000000001",
"expiresAt": 1780000900000,
"summary": {
"total": 2,
"executable": 1,
"approve": 1,
"reject": 0,
"noAction": 0,
"invalid": 1
},
"rows": [
{
"rowNumber": 2,
"identifier": "<SampleTaxId1>",
"period": "140501",
"issuanceDate": 1780000000000,
"action": "APPROVE",
"rejectReasons": [],
"rejectDescription": null,
"errors": [],
"warnings": [],
"previewStatus": "EXECUTABLE",
"previewCode": null,
"previewMessage": null
},
{
"rowNumber": 3,
"identifier": "<SampleTaxId2>",
"action": "REJECT",
"errors": ["برای رد صورتحساب حداقل یک دلیل لازم است."],
"warnings": [],
"previewStatus": "VALIDATION_ERROR",
"previewCode": "invalid_excel_row",
"previewMessage": "برای رد صورتحساب حداقل یک دلیل لازم است."
}
]
},
"errors": [],
"fieldErrors": [],
"correlationId": "00000000-0000-0000-0000-000000000001"
}
{
"status": "OK",
"data": {
"batchCorrelationId": "00000000-0000-0000-0000-000000000001",
"resultToken": "<ResultToken>",
"summary": {
"total": 2,
"success": 1,
"failed": 1,
"skipped": 0,
"batchCorrelationId": "00000000-0000-0000-0000-000000000001",
"results": [
{
"identifier": "<SampleTaxId1>",
"period": "140501",
"issuanceDate": 1780000000000,
"action": "APPROVE",
"status": "SUCCESS",
"code": null,
"message": "عملیات انجام شد.",
"correlationId": "00000000-0000-0000-0000-000000000001"
},
{
"identifier": "<SampleTaxId2>",
"action": "REJECT",
"status": "VALIDATION_ERROR",
"code": "invalid_excel_row",
"message": "برای رد صورتحساب حداقل یک دلیل لازم است.",
"correlationId": "00000000-0000-0000-0000-000000000001"
}
]
}
},
"errors": [],
"fieldErrors": [],
"correlationId": "00000000-0000-0000-0000-000000000001"
}
.xlsx با Content-Type رسمی application/vnd.openxmlformats-officedocument.spreadsheetml.sheet یا fallback رایج application/octet-stream پذیرفته میشود. در هر دو حالت پسوند، ZIP/OpenXML magic و ساختار package در Backend کنترل میشود. هدر Content-Length برای multipart الزامی است و Upload chunked بدون آن با HTTP 411 رد میشود. فایل Macro، رمزدار، دارای پسوند یا Magic نامعتبر، Formula قابل ارزیابی، Duplicate ID یا بیش از سقف Size/Rows رد میشود. مقدار پیشفرض Upload پنج MiB و حداکثر قابل تنظیم ۵۰ MiB است؛ سقف IIS فقط برای مسیر Tax Workspace روی ۵۱ MiB تنظیم شده تا overhead multipart را پوشش دهد. حداکثر ردیف پیشفرض ۵۰۰، حداکثر عملیات قابل اجرای صورتحساب و قرارداد در هر نوبت بهصورت پیشفرض ۲۰ و عمر Token عملیات ۱۵ دقیقه است. Claim اجرای Excel برای replay ایمن و دریافت Result بهصورت پیشفرض ۲۴ ساعت نگهداری میشود؛ مقدار نهایی میتواند در Production محدودتر باشد.
نمونه cURL
curl -X PUT "https://api-v2.asatsp.ir/api/v2/tax-workspace/invoices/bulk" \
-H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
-H "X-Correlation-ID: 00000000-0000-0000-0000-000000000001" \
-H "Content-Type: application/json; charset=utf-8" \
-d '{"kind":"purchase","taxIds":["<TaxId1>","<TaxId2>"],"action":"APPROVE"}'
curl -X POST "https://api-v2.asatsp.ir/api/v2/tax-workspace/invoices/purchase/excel/preview" \
-H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
-F "file=@operations.xlsx;type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
curl -X POST "https://api-v2.asatsp.ir/api/v2/tax-workspace/invoices/purchase/excel/execute" \
-H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
-H "Content-Type: application/json" \
-d '{"previewToken":"<PreviewToken>"}'
curl -X POST "https://api-v2.asatsp.ir/api/v2/tax-workspace/excel/result" \
-H "Authorization: Bearer <AccessToken with v2.invoice.read>" \
-H "Content-Type: application/json" \
-d '{"resultToken":"<ResultToken>"}' \
--output tax-workspace-result.xlsx
Status و Error Codeها
| HTTP | Code نمونه | معنا |
|---|---|---|
200 | invalid_excel_row / invoice_not_awaiting_reaction / contract_not_actionable / tax_workspace_contract_reactions_disabled / tax_workspace_contract_permission_missing | خطای Validation، Actionability، Feature یا Delegation Permission در مسیرهای گروهی خطای HTTP کل Batch نیست: Preview آن را در data.rows[].previewCode و previewStatus، JSON Bulk در data.results[] و Execute در data.summary.results[] برمیگرداند. |
400 | invalid_bulk_size / invalid_excel_preview_token / tax_workspace_excel_file_required | اندازه Batch، Token پیشنمایش یا ساختار multipart نامعتبر است. |
401 | unauthorized | Access Token/API Key ارسال نشده یا معتبر نیست. |
403 | forbidden / tax_workspace_excel_token_context_mismatch | Scope داخلی لازم وجود ندارد، یا Token متعلق به Company، API Client، کارپوشه یا نوع عملیات جاری نیست. |
408 | request_cancelled | درخواست در زمان پردازش لغو شده یا زمان آن به پایان رسیده است. |
409 | tax_workspace_excel_export_row_limit_exceeded / tax_workspace_invoice_bulk_action_limit_exceeded / tax_workspace_contract_bulk_action_limit_exceeded / tax_workspace_excel_execution_in_progress / tax_workspace_excel_execution_outcome_unknown / tax_workspace_excel_token_expired / tax_workspace_excel_result_expired | سقف نتیجه یا عملیات رد شده، اجرای همان Preview در حال انجام یا نتیجه قبلی نامشخص است، یا مهلت Preview/Result پایان یافته است. این پاسخها مجوز Retry کور PUT نیستند. |
411 | tax_workspace_excel_content_length_required | ارسال Content-Length برای Upload الزامی است. |
413 | tax_workspace_excel_file_too_large / tax_workspace_excel_row_limit_exceeded | حجم فایل یا تعداد ردیفهای Upload از سقف تنظیمشده بیشتر است. |
415 | tax_workspace_excel_invalid_content_type | فایل multipart، پسوند یا Content-Type معتبر نیست. |
503 | service_not_configured / organization_unavailable | Host امن تنظیم نشده یا سرویس سازمان موقتاً در دسترس نیست. |
همه پاسخهای JSON از Envelope شامل status، data، errors[]، fieldErrors[] و correlationId استفاده میکنند. همان Correlation در هدر X-Correlation-ID نیز بازگردانده میشود. فایلهای موفق با HTTP 200 و Content-Type رسمی xlsx برمیگردند؛ خطای تولید یا دسترسی فایل همیشه JSON است.
TaxWorkspace.ContractReactionsEnabled فقط اجرای Approve/Reject قرارداد را متوقف میکند؛ مشاهده فهرست و جزئیات، Filter، Pagination، Export، Template، صورتحسابها و Delegation موجود همچنان فعال میمانند. Preview فایل همچنان Parse و خوانده میشود، اما ردیفهای دارای Action با وضعیت Feature disabled غیرقابل اجرا گزارش میشوند. اگر Flag روشن ولی Permission واگذارشده موجود نباشد، پاسخ باید حالت Permission missing را از Feature disabled متمایز کند.
چکلیست پیش از ثبت یا ارسال
| کنترل | نتیجه مورد انتظار |
|---|---|
| هدر احراز هویت | Authorization: Bearer <AccessToken> ارسال شده باشد. در دوره مهاجرت، Customer API Key legacy هم تا تاریخ sunset پذیرفته میشود. |
| شناسه فروشنده | Header.tins با کد اقتصادی شرکت صاحب کلید برابر باشد. |
| یکتایی درخواست | برای هر درخواست جدید یک uuid تازه تولید شده باشد و همه internalIdها برای شرکت احراز هویتشده یکتا باشند. |
| ساختار صورتحساب | هر عضو data[] دارای Header و Body غیرخالی باشد. |
| ماده ۹ | برای صورتحساب خارج از مهلت ۱۵ روز، Header.insr=1 و Header.indati2m معتبر را همزمان ارسال کنید. صورتحساب عادی و کلاینت قدیمی به این دو فیلد نیاز ندارند. |
| جمع ردیفها | adis، tsstam و جمعهای Header دقیقاً با Body برابر باشند. |
| تسویه و پرداخت | cap + insp <= tbill و مجموع پرداختها از tbill بیشتر نباشد. |
| الگوهای خاص | فیلدهای اختصاصی ارز، طلا، پیمانکاری، قبوض، پرواز، صادرات، بارنامه، فرآوردههای نفتی، اوراق بهادار مبتنی بر کالا، بیمه و فروش زنجیره تکمیل شده باشند. |
| الگوی ۱۱ (بورس کالا) | adis (در مدل ساده amountAfterDiscount) دقیقاً برابر ارزش معامله در اعلامیه فروش بورس و Header.tadis برابر آن باشد؛ fee/unitPrice، prdis و dis ارسال نشده باشند. جزئیات در مبلغ واحد در الگوی ۱۱. |
| نگهداری شناسهها | uuid، internalId، traceId و در پاسخ موفق asatspId را ذخیره کنید. |