ثبت سفارش و پرداخت
سوپر اپلیکیشن مهراد
این سند فرآیند کامل ایجاد سفارش، پرداخت توسط کاربر، تأیید پرداخت و عملیات بعدی سفارش (ارسال، تحویل، اصلاح و لغو) را برای جریان آنلاین توضیح میدهد. جریان حالتی که مشتری به صورت فیزیکی در فروشگاه پذیرنده حاضر است، در مستندات خرید حضوری توضیح داده شده است.
نمای کلی
کاربری که با ورود یکپارچه وارد سایت پذیرنده شده، سبد خریدش را میبندد و به جای درگاه بانکی، با داراییهای مهراد پرداخت میکند. نقش پذیرنده در این جریان:
پذیرندهایجاد پرداخت
POST/gateway/order/payment/requestپذیرندههدایت کاربر به payment_url که API برگردانده است
کاربرپرداخت در اپلیکیشن مهراد
کاربربازگشت به callback_url پذیرنده
این بازگشت تضمینشده نیست.
پذیرندهتأیید پرداخت
POST/gateway/order/payment/verifyالزامی؛ حداکثر تا ۳۰ دقیقه پس از ایجاد پرداخت.
پذیرندهتحویل کالا/خدمت و در ادامه ارسال، تحویل، اصلاح یا لغو سفارش
| مرحله | اقدام | endpoint |
|---|---|---|
| ۱ | ایجاد سفارش و تلاش پرداخت | POST /gateway/order/payment/request |
| ۲ | هدایت کاربر به صفحهٔ پرداخت | آدرس payment_url از response مرحلهٔ ۱ |
| ۳ | دریافت بازگشت کاربر | callback_url پذیرنده |
| ۴ | استعلام وضعیت (در صورت نیاز) | GET /gateway/order/payment/{authority}/status |
| ۵ | تأیید پرداخت | POST /gateway/order/payment/verify |
| ۶ | عملیات بعدی سفارش | shipped / deliver / update / cancel |
پیشنیازها
| مورد | توضیح |
|---|---|
| Access Token از SSO | برای ایجاد پرداخت لازم است؛ نحوهٔ دریافت در مستندات SSO |
scope order | برای ایجاد پرداخت باید این scope برای پذیرنده فعال باشد |
| token اپلیکیشن پذیرنده | برای تأیید پرداخت و عملیات بعدی سفارش؛ مستندات ارتباط پذیرندگان |
callback_url | آدرسی در سایت پذیرنده برای بازگشت کاربر پس از پرداخت |
| ID یکتای سفارش | external_order_id در سیستم پذیرنده |
فهرست endpointها
| endpoint | کاربرد | authentication |
|---|---|---|
POST /gateway/order/payment/request | ایجاد سفارش و تلاش پرداخت | Access Token از SSO + scope order |
POST /gateway/order/payment/verify | تأیید پرداخت | token اپلیکیشن پذیرنده یا Access Token از SSO |
GET /gateway/order/payment/{authority}/status | استعلام وضعیت پرداخت | token اپلیکیشن پذیرنده یا Access Token از SSO |
POST /gateway/order/{order_id}/shipped | ثبت ارسال سفارش | token اپلیکیشن پذیرنده |
POST /gateway/order/{order_id}/deliver | ثبت تحویل سفارش | token اپلیکیشن پذیرنده |
POST /gateway/order/{order_id}/update | اصلاح کاهشی سفارش | token اپلیکیشن پذیرنده |
GET /gateway/order/{order_id} | دریافت جزئیات و تاریخچهٔ سفارش | token اپلیکیشن پذیرنده |
POST /gateway/order/{order_id}/cancel | لغو سفارش و بازگشت کامل وجه | token اپلیکیشن پذیرنده |
برای endpointهایی که هر دو روش را میپذیرند، استفاده از token اپلیکیشن پذیرنده توصیه میشود؛ این token به کاربر خاصی وابسته نیست و از سمت سرور پذیرنده مدیریت میشود.
مرحله ۱ — ایجاد پرداخت
/gateway/order/payment/requestAuthentication: Access Token دریافتشده از SSO در header Authorization.
Scope لازم: این endpoint علاوه بر token معتبر، به scope order نیاز دارد. این scope باید برای ترکیب پذیرنده و سرویس فعال شده باشد. اگر token این scope را نداشته باشد، response آن با کد ۴۰۳ و خطای SSO_INSUFFICIENT_SCOPE بازگردانده میشود.
Authorization: Bearer <access_token>ID کاربر، پذیرنده و سرویس از همین token استخراج میشود و نباید در body ارسال شود.
ورودی
amountintegerالزامیexternal_ order_ idstringالزامیcallback_ urlstringالزامیshipmentsarray of objectsفیلدهای هر عضو لیست shipments3 فیلد
idstringالزامیshipping_ feeintegerپیشفرض: 0
itemsarray of objectsالزامیفیلدهای هر عضو لیست items5 فیلد
productobjectالزامیفیلدهای product6 فیلد
idstringالزامیtitlestringالزامیurlstringالزامیis_ domesticbooleanimageobjectالزامیفیلدهای image2 فیلد
urlstringالزامیthumbnail_ urlstringالزامیvariantobjectالزامیفیلدهای variant2 فیلد
idstringالزامیtitlestringالزامیpriceintegerالزامیquantityintegerپیشفرض: 1
categorystringالزامیextra_ dataobjectservicesarray of objectsفیلدهای هر عضو لیست services2 فیلد
titlestringالزامیamountintegerالزامیdiscount_ amountintegerdescriptionstringorder_ urlstringexternal_ payment_ idstringروشهای ثبت اقلام
اقلام سفارش به دو روش قابل ثبت است. ثبت در قالب مرسوله (shipments) ساختار فعلی است. items منسوخ است و در نسخههای آینده حذف خواهد شد.
| روش | فیلد request | وضعیت |
|---|---|---|
| ۱. اقلام بهتفکیک مرسوله | shipments | ✅ روش فعلی |
| ۲. اقلام بدون مرسوله (ساختار پیشین) | items | ⚠️ منسوخ (deprecated)؛ در نسخههای آینده حذف خواهد شد |
در یک request باید یا shipments یا items ارسال شود؛ اگر هر دو با هم ارسال شوند، response آن با کد ۴۲۲ و خطای VALIDATION_ERROR برگردانده میشود.
روش فعلی: shipments
در این روش، اقلام سفارش در یک یا چند مرسوله گروهبندی میشوند. هر مرسوله شناسه (id)، هزینهٔ ارسال (shipping_fee) و فهرست اقلام مختص به خود را دارد و وضعیت ارسال و تحویل آن بهصورت مستقل ثبت میشود؛ از این رو، سفارشهایی که در چند بسته یا از چند انبار ارسال میشوند، بهدرستی قابل نمایش هستند. هر قلم مرسوله شامل اطلاعات کامل کالا (product)، تنوع (variant) و تصویر کالاست.
idهر مرسوله در یک سفارش یکتاست؛ اگر تکرار شود، response آن با کد ۴۲۲ و خطایVALIDATION_ERRORبرگردانده میشود.shipping_feeبه مبلغ سفارش افزوده میشود و نباید منفی باشد. در صورت نبود هزینهٔ ارسال، این فیلد میتواند صفر باشد یا ارسال نشود.- مرسولهای که تنها هزینهٔ ارسال دارد، میتواند با
itemsخالی ارسال شود. quantityاگر نیاید ۱ است و حداقل مقدار آن ۱ است.- مجموع هزینهٔ ارسال مرسولهها همراه با
servicesدر فیلدservice_feeسفارش گزارش میشود. - ثبت ارسال و تحویل (مرحلهٔ ۶) برای هر مرسوله جداگانه و با
idهمان مرسوله انجام میشود.
تفاوت کالا (product) و تنوع (variant): product.id معرف کالا بهطور کلی است (مانند «کفش مدل X») و variant گونهٔ خریداریشدهٔ آن کالا را مشخص میکند (مانند رنگ یا سایز معین). در صورت خرید چند تنوع از یک کالا، هر تنوع باید بهعنوان قلمی جداگانه با product.id یکسان و variant.id متفاوت ارسال شود. اگر کالا تنوعی ندارد، ارسال variant با id و title باز هم الزامی است.
روش منسوخ: items
این روش، ساختار پیشین ثبت اقلام است و صرفاً برای حفظ سازگاری با یکپارچهسازیهای موجود پشتیبانی میشود؛ این ساختار در نسخههای آینده حذف خواهد شد. پیادهسازیهای جدید باید از روش اصلی (shipments) استفاده کنند و پیادهسازیهای موجود نیز لازم است به این روش منتقل شوند.
ساختار هر قلم در items
در این روش، اقلام بدون مرسوله و هر قلم تنها با عنوان، قیمت و اطلاعات پایهٔ کالا (بدون product و variant) ارسال میشود. سفارش در مهراد با یک مرسولهٔ بدون شناسه و بدون هزینهٔ ارسال شامل همهٔ اقلام ثبت میشود؛ هزینهٔ ارسال در این روش از طریق services اعلام میشود. این فیلد تنها ساختار زیر را میپذیرد و اقلام با ساختار shipments[].items (دارای product) در آن معتبر نیستند.
titlestringالزامیpriceintegerالزامیquantityintegerپیشفرض: 1
product_ idstringcategorystringurlstringimage_ urlstringفیلد
brand_originدیگر پذیرفته نمیشود؛ در صورت ارسال، نادیده گرفته میشود و در سفارش ذخیره نخواهد شد. معادل آن درshipments،product.is_domesticاست.
برای مهاجرت به shipments، فیلدهای items به این صورت متناظرند:
items[] | shipments[].items[] |
|---|---|
title | product.title |
product_id | product.id |
url | product.url |
image_url | product.image.url |
category (اختیاری) | category |
در items، فیلدهای is_domestic، image.thumbnail_url، variant و extra_data وجود ندارند و category اختیاری است.
نمونهٔ یک قلم در items:
{
"title": "یخچال ساید بای ساید",
"product_id": "p-1234",
"url": "https://shop.example.com/product/1234",
"image_url": "https://shop.example.com/media/1234.webp",
"price": 10000000,
"quantity": 1
}کالاهای تکراری
اگر دو قلم با همان product_id/product.id و همان variant_id (یا هر دو بدون تنوع) در یک request بیایند، حتی اگر در مرسولههای متفاوت باشند، کل request با خطای DUPLICATE_ORDER_ITEM رد میشود. دو قلم از یک کالا با تنوعهای متفاوت مجازند و دو قلم جداگانه محسوب میشوند. اقلامی که هیچ ID کالایی ندارند (نه product_id و نه product.id) هرگز برای تکراری بودن بررسی نمیشوند، حتی اگر عنوان یکسانی داشته باشند.
قاعدهٔ مبلغ
مقدار amount باید دقیقاً با مجموع اقلام، هزینهٔ ارسال مرسولهها و خدمات منهای تخفیف برابر باشد:
amount = SUM(price × quantity همهٔ اقلام) + SUM(shipments.shipping_fee) + SUM(services.amount) - discount_amountدر صورت عدم تساوی، خطای INVALID_ORDER_AMOUNT و در صورتی که حاصل صفر یا منفی شود، خطای ORDER_AMOUNT_NOT_POSITIVE برگردانده میشود.
قیمت هر قلم و مبلغ هر خدمت باید بزرگتر از صفر باشد و تخفیف نباید منفی باشد؛ در غیر این صورت response آن با کد ۴۲۲ و خطای VALIDATION_ERROR برگردانده میشود.
نمونه request
{
"amount": 13800000,
"external_order_id": "SHOP-2026-0918-4471",
"external_payment_id": "PAY-4471-1",
"callback_url": "https://shop.example.com/payment/callback",
"order_url": "https://shop.example.com/orders/4471",
"description": "خرید کالا از فروشگاه",
"shipments": [
{
"id": "S1",
"shipping_fee": 1000000,
"items": [
{
"product": {
"id": "p-1234",
"title": "یخچال ساید بای ساید",
"url": "https://shop.example.com/product/1234",
"is_domestic": false,
"image": {
"url": "https://shop.example.com/media/1234.webp",
"thumbnail_url": "https://shop.example.com/media/1234-thumb.webp"
},
"variant": {
"id": "p-1234-white",
"title": "سفید"
}
},
"price": 10000000,
"quantity": 1,
"category": "لوازم خانگی برقی,یخچال و فریزر"
}
]
},
{
"id": "S2",
"shipping_fee": 500000,
"items": [
{
"product": {
"id": "p-5678",
"title": "کتری برقی",
"url": "https://shop.example.com/product/5678",
"is_domestic": true,
"image": {
"url": "https://shop.example.com/media/5678.webp",
"thumbnail_url": "https://shop.example.com/media/5678-thumb.webp"
},
"variant": {
"id": "p-5678-steel",
"title": "استیل"
}
},
"price": 2000000,
"quantity": 1,
"category": "لوازم خانگی برقی,کتری"
}
]
}
],
"services": [
{
"title": "هزینه بستهبندی",
"amount": 800000
}
],
"discount_amount": 500000
}محاسبهٔ مبلغ این نمونه:
(10000000 × 1) + (2000000 × 1) + (1000000 + 500000) + 800000 - 500000 = 13800000Response
order_ iduuidpayment_ urlstringtracking_ codestringamountintegerdescriptionstring | nullcreated_ atdatetime{
"success": true,
"data": {
"authority": "8cdb1c8c-bd3c-4f0f-9c8b-9c72c1f2e41c",
"order_id": "1f9d3c47-6a20-4bd1-8f5e-77c0ab93e412",
"payment_url": "https://pwa.mehrad.app/payment/?authority=8cdb1c8c-bd3c-4f0f-9c8b-9c72c1f2e41c&amount=13800000",
"tracking_code": "20463158",
"description": null,
"amount": 13800000,
"created_at": "2026-09-18T11:34:21.482000+00:00"
},
"meta": null
}پردازش response
authorityوorder_idباید کنار سفارش پذیرنده ذخیره شوند؛authorityبرای استعلام و تأیید پرداخت وorder_idبرای عملیات بعدی سفارش لازم است.payment_urlباید همانطور که برگشته استفاده شود. ساختار این آدرس بخشی از قرارداد نیست؛ نباید ساخته یا تغییر داده شود و هیچ parameter دیگری نیز نباید به آن اضافه شود.tracking_codeباید برای پشتیبانی و پیگیری نگهداری شود.
نکتهٔ مهم دربارهٔ external_order_id
این مقدار باید برای هر سفارش یکتا باشد. اگر برای مقداری که به یک سفارش پرداختشده تعلق دارد دوباره request پرداخت ارسال شود، خطای ORDER_ALREADY_PAID برگردانده میشود. اگر این مقدار به سفارشی پرداختنشده تعلق داشته باشد، سفارش تازهای ساخته نمیشود؛ همان سفارش قبلی از روی request جدید (مبلغ، اقلام، خدمات و تخفیف) بازتعریف و یک تلاش پرداخت جدید برای آن ساخته میشود. در غیر این صورت، سفارش جدید ایجاد میشود. سفارشی که متعلق به کاربر دیگری باشد با خطای ORDER_USER_NOT_MATCHED رد میشود.
این رفتار برای «تلاش مجدد پرداخت روی همان سفارش» طراحی شده است؛ اما استفادهٔ ناخواسته از آن موجب ادغام سفارشهای متفاوت پذیرنده در مهراد و جایگزینی محتوای سفارش قبلی با request جدید میشود.
مرحله ۲ — هدایت کاربر به صفحهٔ پرداخت
کاربر باید به payment_url هدایت شود. در ادامه:
- کاربر باید در اپلیکیشن مهراد وارد حساب خود باشد؛ در غیر این صورت ابتدا وارد میشود و سپس صفحهٔ پرداخت را میبیند.
- کاربر حسابهای خود را انتخاب و پرداخت را انجام میدهد. اگر موجودی کافی نباشد، ممکن است ابتدا از درگاه بانکی کیف پولش را شارژ کند و سپس برگردد؛ این کار زمان کل فرآیند را طولانیتر میکند.
- کاربر میتواند پرداخت را لغو کند.
مرحله ۳ — بازگشت کاربر به سایت پذیرنده
پس از پایان کار، کاربر به callback_url پذیرنده هدایت میشود و این parameterها به آدرس اضافه میشوند (parameterهای خودِ آدرس پذیرنده حفظ میشوند):
| parameter | مقدار |
|---|---|
authority | ID تلاش پرداخت |
status | success یا failed |
نمونه:
https://shop.example.com/payment/callback?authority=8cdb1c8c-bd3c-4f0f-9c8b-9c72c1f2e41c&status=successاین بازگشت سند پرداخت نیست
status=success در آدرس بازگشت فقط یک نشانه برای بهبود تجربهٔ کاربر است و نباید مبنای تحویل کالا یا ثبت پرداخت موفق قرار گیرد. دلایل:
- ممکن است کاربر اصلاً به سایت پذیرنده بازنگردد؛ بستن مرورگر، قطع اینترنت یا رها کردن صفحه کاملاً ممکن است.
- بازگشت بلافاصله انجام نمیشود: کاربر ابتدا رسید پرداخت را در اپلیکیشن میبیند و انتقال خودکار با تأخیری حدود ۱۰ ثانیه انجام میشود (کاربر میتواند دکمهٔ بازگشت را هم بزند). این زمان رفتار مشاهدهشدهٔ اپلیکیشن است، نه تعهد زمانی API.
- آدرس بازگشت در مرورگر کاربر است و قابل دستکاری است.
قاعدهٔ درست: با دریافت بازگشت کاربر، وضعیت باید از API دریافت شود (مرحلهٔ ۴) و تنها پس از تأیید موفق (مرحلهٔ ۵) سفارش پرداختشده در نظر گرفته شود. اگر کاربر بازنگشت، همین بررسی باید از سمت سرور و بر اساس authority ذخیرهشده انجام شود.
مرحله ۴ — استعلام وضعیت پرداخت
/gateway/order/payment/{authority}/statusAuthentication: token اپلیکیشن پذیرنده یا Access Token از SSO.
{
"success": true,
"data": {
"ref_id": "5f7d1a30-1c77-4a2b-b6d1-0f3f41f9c5e2",
"status": "PAID",
"fee": 15500
},
"meta": null
}status: وضعیت فعلی تلاش پرداخت (جدول وضعیتها در ادامه).ref_id: ID تراکنش سفارش؛ تا وقتی تراکنشی ثبت نشده باشد رشتهٔ خالی برمیگردد.fee: کارمزد سامانه برای این تلاش پرداخت.
اگر status برابر PAID باشد، سفارش پرداخت شده و منتظر تأیید پذیرنده است؛ ادامه در مرحلهٔ ۵.
مرحله ۵ — تأیید پرداخت
/gateway/order/payment/verifyAuthentication: token اپلیکیشن پذیرنده یا Access Token از SSO. استفاده از token اپلیکیشن پذیرنده توصیه میشود.
تأیید پرداخت الزامی است. تا زمانی که تأیید انجام نشود، مبلغ برای پذیرنده قطعی نمیشود.
ورودی
amountintegerالزامی{
"authority": "8cdb1c8c-bd3c-4f0f-9c8b-9c72c1f2e41c",
"amount": 13800000
}Response
{
"success": true,
"data": {
"ref_id": "5f7d1a30-1c77-4a2b-b6d1-0f3f41f9c5e2",
"status": "VERIFY_OK",
"fee": 15500
},
"meta": null
}status برابر VERIFY_OK یعنی پرداخت نهایی شده است. ref_id، ID تراکنش است و برای تطبیق حساب باید ذخیره شود.
مهلت ۳۰ دقیقهای
تأیید باید حداکثر تا ۳۰ دقیقه پس از ایجاد تلاش پرداخت انجام شود. پس از آن پرداخت به صورت خودکار بسته میشود: مبلغ به حساب کاربر بازمیگردد و وضعیت تلاش پرداخت VERIFY_TIMEOUT میشود. تلاش پرداختی که هنوز پرداخت نشده باشد، پس از همین مهلت CANCELED میشود.
هشدار دربارهٔ مبلغ
اگر مبلغ ارسالی با مبلغ ثبتشدهٔ تلاش پرداخت برابر نباشد، تأیید رد میشود، تلاش پرداخت باطل میشود (VERIFY_FAILED) و وجه به کاربر بازمیگردد؛ امکان تأیید دوبارهٔ همان پرداخت وجود ندارد. اگر سفارش متعلق به پذیرندهٔ دیگری باشد، request با ORDER_MERCHANT_NOT_MATCHED رد میشود و وضعیت پرداخت تغییر نمیکند. مبلغ ارسالی همواره باید همان مبلغی باشد که در response مرحلهٔ ۱ یا استعلام وضعیت آمده است.
تلاش مجدد
- اگر request تأیید با خطای شبکه مواجه شود یا response آن کد ۵xx داشته باشد، ارسال مجدد آن با همان
authorityو همان مبلغ مجاز است. - دریافت خطای
ORDER_ALREADY_PAIDبه این معناست که این پرداخت قبلاً تأیید شده است؛ در این حالت پرداخت موفق در نظر گرفته میشود و تأیید نباید تکرار شود. - دریافت خطای
ORDER_ATTEMPT_NOT_PAIDبه این معناست که کاربر هنوز پرداخت را کامل نکرده است؛ در این حالت request نباید بلافاصله تکرار شود و وضعیت باید از طریق endpoint استعلام پیگیری شود.
مرحله ۶ — عملیات بعدی سفارش
پس از تأیید پرداخت، چرخهٔ عمر سفارش با این عملیات ادامه پیدا میکند. همهٔ آنها فقط با token اپلیکیشن پذیرنده کار میکنند و روی order_id (نه authority) عمل میکنند.
ارسال و تحویل
ارسال و تحویل برای هر مرسوله جداگانه ثبت میشود. وضعیت کل سفارش از وضعیت مرسولهها نتیجه میشود: shipped_at سفارش زمانی مقدار میگیرد که آخرین مرسولهاش ارسال شده باشد، و delivered_at سفارش زمانی که آخرین مرسولهاش تحویل شده باشد. مرسولهها به هر ترتیبی میتوانند ارسال و تحویل شوند.
هر دو endpoint یک body اختیاری میگیرند که مرسوله را مشخص میکند:
shipment_ idstring- سفارشی که با
shipmentsثبت شده است:shipment_idالزامی است. ارسال نکردن آن یا ارسال شناسهای که در سفارش نیست، خطایORDER_SHIPMENT_NOT_FOUNDمیدهد. - سفارشی که با
itemsثبت شده است: body ارسال نمیشود و عملیات روی همان مرسولهٔ بدون شناسه انجام میشود. ارسالshipment_idبرای آن،ORDER_SHIPMENT_NOT_FOUNDاست. - سفارشی که هیچ مرسولهای ندارد: body ارسال نمیشود و ارسال و تحویل روی خود سفارش ثبت میشود.
- مرسولهای که در یک اصلاح کاهشی از سفارش حذف شده باشد دیگر قابل ارسال یا تحویل نیست و
ORDER_SHIPMENT_NOT_FOUNDمیگیرد. - اگر در یک اصلاح کاهشی، آخرین مرسولهٔ ارسالنشده (یا تحویلنشده) حذف شود و مرسولههای باقیمانده همه ارسال (یا تحویل) شده باشند، سفارش بهطور خودکار ارسالشده (یا تحویلشده) محسوب میشود.
ثبت ارسال
/gateway/order/{order_id}/shipped{
"shipment_id": "S1"
}برای مرسولهای که قبلاً ارسال شده باشد، خطای ORDER_ALREADY_SHIPPED برگردانده میشود. response، جزئیات سفارش است (ساختار آن در بخش «جزئیات و تاریخچهٔ سفارش» آمده)؛ زمان ارسال هر مرسوله در versions[].shipments[].shipped_at و زمان ارسال کل سفارش در shipped_at است.
{
"success": true,
"data": {
"id": "1f9d3c47-6a20-4bd1-8f5e-77c0ab93e412",
"external_order_id": "SHOP-2026-0918-4471",
"status": "SUCCESS",
"amount": 13800000,
"version": 1,
"is_shipped": false,
"shipped_at": null,
"is_delivered": false,
"delivered_at": null,
"versions": [
{
"version": 1,
"shipments": [
{ "external_id": "S1", "shipping_fee": 1000000, "shipped_at": "2026-09-19T08:05:44.120000+00:00", "delivered_at": null },
{ "external_id": "S2", "shipping_fee": 500000, "shipped_at": null, "delivered_at": null }
]
}
]
},
"meta": null
}در این نمونه فقط مرسولهٔ S1 ارسال شده است؛ تا زمانی که S2 هم ارسال نشود، is_shipped و shipped_at سفارش مقدار ندارند. (برای کوتاهشدن، بیشتر فیلدهای response در نمونه حذف شده است.)
ثبت تحویل
/gateway/order/{order_id}/deliverbody مانند ارسال است (shipment_id). پیشنیاز: همان مرسوله باید قبلاً ارسال شده باشد؛ در غیر این صورت خطای ORDER_NOT_SHIPPED_YET برگردانده میشود. برای مرسولهای که قبلاً تحویل شده باشد، خطای ORDER_ALREADY_DELIVERED برگردانده میشود. ساختار response مانند ارسال است و delivered_at مقدار میگیرد.
بازگشت وجه
وجه سفارش پرداختشده به دو روش به کاربر بازمیگردد:
| بازگشت بخشی (اصلاح کاهشی) | بازگشت کامل (لغو) | |
|---|---|---|
| endpoint | POST /gateway/order/{order_id}/update | POST /gateway/order/{order_id}/cancel |
| کاربرد | مرجوعی، حذف قلم یا مرسوله، یا کاهش هزینهٔ ارسال و خدمات | لغو کل سفارش |
| مبلغ بازگشتی | تفاوت ارزش سفارش پیش و پس از اصلاح | تمام وجه سفارش |
| تکرار | چند بار؛ هر بار نسبت به آخرین نسخه | — |
| وضعیت پس از عملیات | سفارش با ارزش کمتر باقی میماند | سفارش با ارزش صفر باقی میماند |
بازگشت بخشی از وجه (اصلاح کاهشی)
/gateway/order/{order_id}/update⚠ این عملیات قابل بازگشت نیست و مبلغ کاهشیافته بلافاصله به همان حسابهای پرداخت کاربر بازمیگردد.
پیشنیازها
- سفارش باید تلاش پرداخت تأییدشده (
VERIFY_OK) داشته باشد، در غیر این صورتORDER_ATTEMPT_NOT_PAID. - ارزش سفارش پس از اصلاح باید اکیداً کمتر از مقدار فعلی باشد؛ اصلاحِ بدون تغییر ارزش با
ORDER_UPDATE_NO_CHANGEرد میشود. - مقادیر فقط میتوانند کاهش یابند.
بدنهٔ request: این body وضعیت کامل سفارش پس از اصلاح را توصیف میکند، نه تغییرات را؛ هر مرسوله یا قلمی که در فهرست نیاید حذف میشود. services اگر نیاید هیچ هزینهٔ جانبی باقی نمیماند. discount_amount اگر نیاید صفر است؛ صفر شدن تخفیفِ موجود ارزش سفارش را بالا میبرد و درخواست رد میشود، مگر کاهش اقلام یا خدمات این افزایش را جبران کند و حاصل همچنان کمتر از ارزش فعلی باشد. ساختار body باید با روش ثبت سفارش یکی باشد: سفارشی که با shipments ثبت شده با shipments و سفارشی که با items ثبت شده با items اصلاح میشود؛ ترکیب غیرمنطبق با ORDER_UPDATE_VALUE_INCREASED رد میشود. نفرستادن هر دو فهرست، همهٔ مرسولهها را حذف میکند.
shipmentsarray of objectsفیلدهای هر عضو لیست shipments3 فیلد
idstringالزامیshipping_ feeintegerپیشفرض: 0
itemsarray of objectsالزامیفیلدهای هر عضو لیست items5 فیلد
productobjectالزامیفیلدهای product6 فیلد
idstringالزامیtitlestringالزامیurlstringالزامیis_ domesticbooleanimageobjectالزامیفیلدهای image2 فیلد
urlstringالزامیthumbnail_ urlstringالزامیvariantobjectالزامیفیلدهای variant2 فیلد
idstringالزامیtitlestringالزامیpriceintegerالزامیquantityintegerپیشفرض: 1
categorystringالزامیextra_ dataobjectservicesarray of objectsفیلدهای هر عضو لیست services2 فیلد
titlestringالزامیamountintegerالزامیdiscount_ amountintegerپیشفرض: 0
external_ version_ idstringreasonstringتطبیق مرسولهها و اقلام (روش shipments): هر مرسوله با id به مرسولهٔ فعلی سفارش تطبیق داده میشود و هر قلم داخل آن با ID کالا (product.id) و ID تنوع (variant.id) با اقلام فعلی همان مرسوله. به این ترتیب:
- مرسولهای که باید در سفارش باقی بماند، با همان
idارسال میشود؛ مرسولهای که در فهرست نیاید حذف و مبلغ اقلام و هزینهٔ ارسال آن بازگردانده میشود. - برای حذف یک قلم، آن قلم در
itemsمرسولهٔ مربوط ارسال نمیشود. مرسولهای که همهٔ اقلام آن حذف شده اما همچنانshipping_feeدارد، باitemsخالی حفظ میشود؛ در صورتی که نه قلمی و نه هزینهای برای آن باقی بماند، از سفارش حذف میشود. shipping_feeفقط میتواند کاهش یابد.- ارسال شناسهٔ مرسولهٔ ناشناخته، انتقال قلم به مرسولهای دیگر یا تکرار یک ترکیب (کالا + تنوع) در دو مرسوله، با خطای
ORDER_UPDATE_VALUE_INCREASEDرد میشود. - وضعیت ارسال و تحویل مرسولههای باقیمانده در نسخهٔ جدید حفظ میشود.
تطبیق اقلام (items): هر قلم در items با ساختار روش منسوخ (product_id، title، price و …) ارسال و بر اساس product_id با اقلام فعلی سفارش تطبیق داده میشود؛ قلم فاقد product_id قابل تطبیق نیست و با خطای ORDER_UPDATE_VALUE_INCREASED رد میشود.
در هر دو روش، مقدار price باید دقیقاً برابر قیمت واحد ثبتشده روی سفارش باشد (ORDER_ITEM_PRICE_MISMATCH) و quantity باید بین ۱ و تعداد فعلی باشد. افزودن قلم جدید، افزایش تعداد یا هزینهٔ ارسال یا خدمات، یا ارسال یک جفت (ID کالا + تنوع) تکراری یا ناشناخته، با ORDER_UPDATE_VALUE_INCREASED رد میشود. برای حذف کامل یک قلم، کافی است آن قلم در فهرست ارسال نشود.
دربارهٔ external_version_id: این مقدار ID اصلاح در سیستم پذیرنده است. اختیاری است، روی نسخهٔ جدید سفارش ثبت و در response عیناً برگردانده میشود و برای تطبیق سوابق اصلاحات میان دو سیستم به کار میرود. این مقدار در سطح هر پذیرنده یکتا است: اگر مقداری که قبلاً برای هر یک از سفارشهای همین پذیرنده ثبت شده دوباره ارسال شود، request با خطای DUPLICATE_EXTERNAL_VERSION_ID رد میشود و اصلاحی انجام نمیگیرد؛ بنابراین برای هر اصلاح باید ID جدیدی تولید شود. در صورت ارسال مجدد request بهدلیل خطای شبکه، دریافت همین خطا نشاندهندهٔ ثبت قبلی اصلاح است. حداکثر طول مقدار ۲۵۵ کاراکتر است.
{
"shipments": [
{
"id": "S1",
"shipping_fee": 600000,
"items": [
{
"product": {
"id": "p-1234",
"title": "یخچال ساید بای ساید",
"url": "https://shop.example.com/product/1234",
"is_domestic": false,
"image": {
"url": "https://shop.example.com/media/1234.webp",
"thumbnail_url": "https://shop.example.com/media/1234-thumb.webp"
},
"variant": {
"id": "p-1234-white",
"title": "سفید"
}
},
"price": 10000000,
"quantity": 1,
"category": "لوازم خانگی برقی,یخچال و فریزر"
}
]
}
],
"discount_amount": 500000,
"external_version_id": "RET-2026-0919-11",
"reason": "مرجوعی کتری، حذف هزینه بستهبندی و کاهش هزینه ارسال یخچال"
}در این نمونه مرسولهٔ S2 (کتری و هزینهٔ ارسالش) و هزینهٔ بستهبندی حذف شده و هزینهٔ ارسال S1 کاهش یافته است. مبلغ کاهشیافتهٔ این نمونه:
(10000000 + 2000000 + 1500000 + 800000 - 500000) - (10000000 + 600000 - 500000) = 3700000Response
{
"success": true,
"data": {
"id": "9c2f1a7e-3b44-4d90-8f11-2ac4d1e77b05",
"order_id": "1f9d3c47-6a20-4bd1-8f5e-77c0ab93e412",
"version": 2,
"external_version_id": "RET-2026-0919-11",
"deducted_amount": 3700000,
"remaining_amount": 10100000,
"reason": "مرجوعی کتری، حذف هزینه بستهبندی و کاهش هزینه ارسال یخچال",
"ref_id": "3a1b9c0d-7e8f-4a2b-9c1d-0e2f3a4b5c6d",
"is_empty": false
},
"meta": null
}deducted_amount: مبلغی که در همین اصلاح به کاربر بازگردانده شد.remaining_amount: ارزش باقیماندهٔ سفارش.ref_id: ID تراکنش بازگشت وجه.is_empty: اگر ارزش سفارش به صفر رسیده باشدtrueاست.
سفارش میتواند طی چند مرحله اصلاح شود؛ هر بار فقط تفاوت نسبت به آخرین نسخه بازگردانده میشود.
لغو کامل سفارش
/gateway/order/{order_id}/cancelکل سفارش لغو و تمام وجه به حساب کاربر بازگردانده میشود. سفارش باید تلاش پرداخت تأییدشده (VERIFY_OK) داشته باشد، در غیر این صورت ORDER_ATTEMPT_NOT_PAID. لغو سفارشی که ارزش آن صفر شده است با ORDER_UPDATE_NO_CHANGE رد میشود.
{
"success": true,
"data": {
"ref_id": "5f7d1a30-1c77-4a2b-b6d1-0f3f41f9c5e2",
"status": "VERIFY_OK",
"fee": 15500
},
"meta": null
}اگر تمام مبلغ سفارش پیشتر بازگردانده شده باشد، response لغو با کد ۴۰۰ و خطای ORDER_UPDATE_NO_CHANGE برگردانده میشود:
{
"success": false,
"error": {
"code": "ORDER_UPDATE_NO_CHANGE",
"message": "وضعیت جدید اعلامشده برای سفارش با وضعیت فعلی آن تفاوتی ندارد.",
"details": null
}
}سایر خطاهای این endpoint در بخش «فهرست خطاها» (لغو سفارش) آمده است.
جزئیات و تاریخچهٔ سفارش
/gateway/order/{order_id}احراز هویت: token اپلیکیشن پذیرنده. order_id همان ID سفارش در مهراد است و سفارش باید متعلق به همین پذیرنده باشد (در غیر این صورت ORDER_NOT_FOUND).
این endpoint وضعیت فعلی سفارش، مبالغ و تاریخچهٔ کامل نسخهها (ایجاد اولیه و هر اصلاح کاهشی) را برمیگرداند. نسخهٔ ۱ همان سفارش اولیه است و هر اصلاح یک نسخهٔ جدید میسازد. اقلام سفارش در هر نسخه داخل shipments همان نسخه قرار دارند؛ آخرین نسخه، مرسولهها و اقلام فعلی سفارش را نشان میدهد. سفارشی که با items (روش منسوخ) ثبت شده، یک مرسولهٔ بدون شناسه (external_id برابر null) دارد.
فیلدهای response
iduuidexternal_ order_ idstringtracking_ codestringchannelstringstatusstringref_ idstring | nullfeeinteger | nullcreated_ atstringserviceobject | nullفیلدهای service3 فیلد
iduuidnamestringslugstringis_ shippedbooleanshipped_ atstring | nullis_ deliveredbooleandelivered_ atstring | nullversionintegerexternal_ version_ idstring | nullamountintegerpaid_ amountintegerrefunded_ amountintegerservice_ feeintegerdiscount_ amountintegerversionsarray of objectsفیلدهای هر عضو لیست versions13 فیلد
iduuidversionintegerexternal_ version_ idstring | nullamountintegerservice_ feeintegerdiscount_ amountintegerreasonstringdeducted_ amountintegerref_ iduuid | nullis_ emptybooleancreated_ atstringdetailsobjectshipmentsarray of objectsفیلدهای هر عضو لیست shipments6 فیلد
iduuidexternal_ idstring | nullshipping_ feeintegershipped_ atstring | nulldelivered_ atstring | nullitemsarray of objectsفیلدهای هر عضو لیست items12 فیلد
iduuidtitlestringpriceintegerquantityintegerexternal_ idstring | nullvariant_ idstring | nullvariant_ titlestring | nullproduct_ urlstring | nullis_ domesticboolean | nullimage_ urlstring | nullthumbnail_ urlstring | nullextra_ dataobject | nullنمونه response
سفارشی با دو مرسوله که مرسولهٔ S1 آن ارسال شده و در یک اصلاح، مرسولهٔ S2 از آن حذف شده است:
{
"success": true,
"data": {
"id": "e03c6ea8-4617-423b-89cb-67b8388480eb",
"external_order_id": "ord-a700fcb49e",
"tracking_code": "27136245",
"channel": "ONLINE",
"status": "SUCCESS",
"ref_id": "98c73b03-087e-4fbd-97e9-637e73ce719b",
"fee": 900,
"created_at": "2026-10-06T07:25:13.498346+00:00",
"service": {
"id": "2f1b7d6e-2f4e-4a1a-9c22-6f0c0a1d5b31",
"name": "فروشگاه اینترنتی",
"slug": "online-shop"
},
"is_shipped": true,
"shipped_at": "2026-10-07T09:10:42.120554+00:00",
"is_delivered": false,
"delivered_at": null,
"version": 2,
"external_version_id": "RET-2026-1006-1",
"amount": 50000,
"paid_amount": 75000,
"refunded_amount": 25000,
"service_fee": 5000,
"discount_amount": 0,
"versions": [
{
"id": "cbdaaab6-e654-4830-988b-b63e404a10a5",
"version": 1,
"external_version_id": null,
"amount": 75000,
"service_fee": 10000,
"discount_amount": 0,
"reason": "initial",
"deducted_amount": 0,
"ref_id": null,
"is_empty": false,
"created_at": "2026-10-06T07:25:13.498346+00:00",
"shipments": [
{
"id": "7b2f6c1e-0a49-4e7c-9a53-2f1d84c0e1aa",
"external_id": "S1",
"shipping_fee": 5000,
"shipped_at": "2026-10-07T09:10:42.120554+00:00",
"delivered_at": null,
"items": [
{
"id": "ea7cf730-9310-4093-ad18-0e4b012f08b3",
"title": "یخچال فریزر ساید بای ساید هیمالیا مدل HI-M900",
"price": 45000,
"quantity": 1,
"external_id": "12acae-A",
"variant_id": "A-silver",
"variant_title": "نقرهای",
"product_url": "https://shop.example.com/products/12acae-A",
"is_domestic": true,
"image_url": "https://shop.example.com/images/12acae-A.png",
"thumbnail_url": "https://shop.example.com/images/12acae-A-thumb.png",
"extra_data": { "warranty_months": 18 }
}
]
},
{
"id": "c1d9e5f3-6a77-4b0c-8e2d-93a1b7f40c52",
"external_id": "S2",
"shipping_fee": 5000,
"shipped_at": null,
"delivered_at": null,
"items": [
{
"id": "54d8c9f4-76a8-4a71-8b75-fdcdf5211049",
"title": "برنج ایرانی هاشمی درجه یک گیلان ۱۰ کیلوگرمی",
"price": 20000,
"quantity": 1,
"external_id": "12acae-B",
"variant_id": "B-10kg",
"variant_title": "۱۰ کیلوگرمی",
"product_url": "https://shop.example.com/products/12acae-B",
"is_domestic": true,
"image_url": "https://shop.example.com/images/12acae-B.png",
"thumbnail_url": "https://shop.example.com/images/12acae-B-thumb.png",
"extra_data": null
}
]
}
],
"details": {
"order_url": "https://shop.example.com/orders/ord-a700fcb49e"
}
},
{
"id": "5d7bf1a9-0166-446d-8b7b-e832d3f703b8",
"version": 2,
"external_version_id": "RET-2026-1006-1",
"amount": 50000,
"service_fee": 5000,
"discount_amount": 0,
"reason": "مرجوعی برنج",
"deducted_amount": 25000,
"ref_id": "3a1b9c0d-7e8f-4a2b-9c1d-0e2f3a4b5c6d",
"is_empty": false,
"created_at": "2026-10-06T07:25:13.973359+00:00",
"shipments": [
{
"id": "0753e4e5-5f9a-43a6-a4af-64a76b9d5c3a",
"external_id": "S1",
"shipping_fee": 5000,
"shipped_at": "2026-10-07T09:10:42.120554+00:00",
"delivered_at": null,
"items": [
{
"id": "70a60588-2dc5-4e71-bae6-1ab4fa15cfe9",
"title": "یخچال فریزر ساید بای ساید هیمالیا مدل HI-M900",
"price": 45000,
"quantity": 1,
"external_id": "12acae-A",
"variant_id": "A-silver",
"variant_title": "نقرهای",
"product_url": "https://shop.example.com/products/12acae-A",
"is_domestic": true,
"image_url": "https://shop.example.com/images/12acae-A.png",
"thumbnail_url": "https://shop.example.com/images/12acae-A-thumb.png",
"extra_data": { "warranty_months": 18 }
}
]
}
],
"details": {
"order_url": "https://shop.example.com/orders/ord-a700fcb49e"
}
}
]
},
"meta": null
}در این نمونه سفارش با دو مرسوله به مبلغ ۷۵٬۰۰۰ پرداخت شده و در یک اصلاح، مرسولهٔ S2 (کالا و هزینهٔ ارسالش، جمعاً ۲۵٬۰۰۰) از آن حذف شده است؛ refunded_amount مجموع deducted_amount نسخههاست. پس از حذف S2، تنها مرسولهٔ باقیمانده (S1) ارسال شده است و به همین دلیل is_shipped سفارش true است. تا زمانی که ارسال یا تحویلی ثبت نشده باشد، is_shipped / is_delivered برابر false و shipped_at / delivered_at برابر null هستند.
ساختار مرسوله (OrderShipment) و کالا (OrderItem) در responseهای سفارش
اقلام یک سفارش در response همیشه داخل مرسوله میآیند (versions[].shipments[].items)، صرف نظر از اینکه سفارش با shipments یا items ثبت شده باشد. هر مرسوله با این ساختار میآید:
iduuidexternal_ idstring | nullshipping_ feeintegershipped_ atstring | nulldelivered_ atstring | nullitemsarray of objectsفیلدهای هر عضو لیست items12 فیلد
iduuidtitlestringpriceintegerquantityintegerexternal_ idstring | nullvariant_ idstring | nullvariant_ titlestring | nullproduct_ urlstring | nullis_ domesticboolean | nullimage_ urlstring | nullthumbnail_ urlstring | nullextra_ dataobject | nullبرای اقلامی که با
items(روش منسوخ) ثبت شدهاند، فیلدهایvariant_id،variant_title،is_domestic،thumbnail_urlوextra_dataدر responsenullخواهند بود.
وضعیتها
وضعیت سفارش (status)
| وضعیت | معنی |
|---|---|
PENDING | سفارش ساخته شده و هنوز پرداخت تأییدشده ندارد |
SUCCESS | پرداخت تأیید شده است |
FAILED | پرداخت انجام یا تأیید نشده و سفارش بسته شده است |
وضعیت تلاش پرداخت
| وضعیت | معنی | اقدام لازم |
|---|---|---|
PENDING | در انتظار پرداخت کاربر | انتظار |
PAID | کاربر پرداخت کرده، در انتظار تأیید پذیرنده | تأیید پرداخت (مرحلهٔ ۵) |
VERIFY_OK | پرداخت تأیید شده | نهاییسازی سفارش |
VERIFY_FAILED | تأیید با مبلغ نادرست انجام شد و پرداخت باطل شد | سفارش جدیدی لازم است |
VERIFY_TIMEOUT | مهلت ۳۰ دقیقهای تأیید گذشت و وجه به کاربر بازگشت | سفارش جدیدی لازم است |
CANCELED | تلاش پرداختِ انجامنشده پس از انقضا توسط سامانه لغو شد | سفارش جدیدی لازم است |
USER_CANCELED | کاربر پرداخت را لغو کرد | سفارش جدیدی لازم است |
یک سفارش میتواند چند تلاش پرداخت داشته باشد؛ برای مثال کاربر یکبار لغو کند و بار دوم پرداخت کند.
فهرست خطاها
اگر request با خطا مواجه شود، response آن کدی غیر از ۲xx دارد و success در آن false است. کد خطا در فیلد error.code و متن قابل نمایش در error.message برگردانده میشود. ساختار کامل خطا در بخش مقدمه آمده است. خطاهای مشترک همهٔ endpointها (VALIDATION_ERROR، FINANCIAL_OPERATIONS_FROZEN و نبود header Authorization) در همان بخش آمدهاند.
خطاهای authentication
| کد خطا | HTTP | endpoint | توضیح و اقدام پیشنهادی |
|---|---|---|---|
| SSO_INVALID_ACCESS_TOKEN | 401 | ایجاد پرداخت | Access Token نامعتبر یا منقضی است؛ کاربر باید دوباره وارد شود. |
| SSO_INSUFFICIENT_SCOPE | 403 | ایجاد پرداخت | token فاقد scope order است و details.required_scope برابر order است؛ فعالسازی آن نیازمند هماهنگی با مهراد است. |
| HTTP_EXCEPTION | 401 | تأیید پرداخت و استعلام وضعیت | header Authorization ارسال نشده، یا token نه token معتبر اپلیکیشن پذیرنده است و نه Access Token معتبر SSO. message در این حالت Unauthorized است. یکبار token تازه باید دریافت و request تکرار شود. |
| MERCHANT_APP_TOKEN_EXPIRED_OR_INVALID | 401 | سایر endpointها | token اپلیکیشن پذیرنده نامعتبر یا منقضی است؛ جزئیات در مستندات ارتباط پذیرندگان. |
| MERCHANT_APP_IP_ADDRESS_NOT_ALLOWED | 403 | سایر endpointها | آیپی request در لیست سفید اپلیکیشن پذیرنده نیست. |
{
"success": false,
"error": {
"code": "HTTP_EXCEPTION",
"message": "Unauthorized",
"details": null
}
}ایجاد پرداخت
/gateway/order/payment/request| کد خطا | HTTP | توضیح و اقدام پیشنهادی |
|---|---|---|
| VALIDATION_ERROR | 422 | ساختار request نامعتبر است؛ برای مثال ارسال همزمان items و shipments یا ارسال نکردن هیچکدام، id تکراری برای مرسولهها، shipping_fee یا تخفیف منفی، قیمت یا مبلغ خدمت صفر یا منفی، تعداد کمتر از ۱، یا callback_url نامعتبر. |
| ORDER_AMOUNT_NOT_POSITIVE | 400 | مبلغ نهایی صفر یا منفی است. |
| DUPLICATE_ORDER_ITEM | 400 | یک کالا با همان ID و همان تنوع بیش از یکبار در سفارش (در items یا در مرسولههای مختلف shipments) آمده؛ اگر این موارد در واقع یک قلم هستند، باید quantity افزایش یابد؛ در غیر این صورت باید variant.id یا product_id/product.id متفاوتی ارسال شود. |
| INVALID_ORDER_AMOUNT | 400 | amount با اقلام، هزینهٔ ارسال مرسولهها، خدمات و تخفیف همخوان نیست. |
| ORDER_ALREADY_PAID | 400 | سفارشی که این external_order_id به آن تعلق دارد قبلاً پرداخت شده است. |
| ORDER_IS_NOT_PENDING | 400 | سفارشی که این external_order_id به آن تعلق دارد بسته شده است (FAILED)؛ برای خرید دوباره باید external_order_id جدیدی ارسال شود. |
| ORDER_USER_NOT_MATCHED | 400 | سفارشی با همین external_order_id قبلاً برای کاربر دیگری ثبت شده است. |
{
"success": false,
"error": {
"code": "INVALID_ORDER_AMOUNT",
"message": "مبلغ ارسالی شما با مبلغ ثبتشده روی سفارش یکی نیست.",
"details": null
}
}تأیید پرداخت
/gateway/order/payment/verify| کد خطا | HTTP | توضیح و اقدام پیشنهادی |
|---|---|---|
| VALIDATION_ERROR | 422 | amount ارسال نشده، یا هیچیک از authority و order_id ارسال نشده یا هر دو با هم ارسال شدهاند. |
| ATTEMPT_NOT_FOUND | 404 | تلاش پرداختی با این authority وجود ندارد. |
| ORDER_NOT_FOUND | 404 | سفارشی با این order_id وجود ندارد. |
| ORDER_MERCHANT_NOT_MATCHED | 400 | سفارش متعلق به پذیرندهٔ دیگری است. |
| ORDER_ALREADY_PAID | 400 | این پرداخت قبلاً تأیید شده است؛ پرداخت موفق در نظر گرفته میشود و تأیید نباید تکرار شود. |
| ORDER_ATTEMPT_NOT_PAID | 400 | پرداختی برای تأیید وجود ندارد؛ کاربر هنوز پرداخت نکرده یا مهلت تأیید گذشته است. وضعیت باید استعلام شود. |
| ORDER_AMOUNT_NOT_MATCHED | 400 | مبلغ تأیید با مبلغ تلاش پرداخت برابر نیست؛ پرداخت باطل و مبلغ به کاربر بازگردانده شد. |
{
"success": false,
"error": {
"code": "ORDER_AMOUNT_NOT_MATCHED",
"message": "مبلغ درخواستی با مبلغ مندرج در فاکتور سفارش متفاوت است.",
"details": null
}
}استعلام وضعیت پرداخت
/gateway/order/payment/{authority}/status| کد خطا | HTTP | توضیح و اقدام پیشنهادی |
|---|---|---|
| VALIDATION_ERROR | 422 | authority قالب UUID ندارد. |
| ATTEMPT_NOT_FOUND | 404 | تلاش پرداختی با این authority وجود ندارد. |
| ORDER_MERCHANT_NOT_MATCHED | 400 | تلاش پرداخت متعلق به پذیرندهٔ دیگری است. |
{
"success": false,
"error": {
"code": "ATTEMPT_NOT_FOUND",
"message": "پرداخت مرتبط با این سفارش وجود ندارد.",
"details": null
}
}ثبت ارسال و تحویل
/gateway/order/{order_id}/shipped/gateway/order/{order_id}/deliver| کد خطا | HTTP | توضیح و اقدام پیشنهادی |
|---|---|---|
| VALIDATION_ERROR | 422 | order_id قالب UUID ندارد. |
| ORDER_NOT_FOUND | 404 | سفارشی با این order_id وجود ندارد. |
| ORDER_MERCHANT_NOT_MATCHED | 400 | سفارش متعلق به پذیرندهٔ دیگری است. |
| ORDER_ATTEMPT_NOT_PAID | 400 | سفارش در وضعیت SUCCESS نیست؛ ارسال و تحویل فقط برای سفارش تأییدشده ثبت میشود. |
| ORDER_SHIPMENT_NOT_FOUND | 404 | مرسولهای با این shipment_id در سفارش نیست؛ یا برای سفارشِ دارای مرسوله، shipment_id ارسال نشده، یا برای سفارشِ بدون شناسهٔ مرسوله ارسال شده است. |
| ORDER_ALREADY_SHIPPED | 400 | این مرسوله (یا سفارش) قبلاً ارسال شده است (فقط در ثبت ارسال). |
| ORDER_ALREADY_DELIVERED | 400 | این مرسوله (یا سفارش) قبلاً تحویل شده است (فقط در ثبت تحویل). |
| ORDER_NOT_SHIPPED_YET | 400 | پیش از ثبت تحویل، ارسال همان مرسوله باید ثبت شده باشد (فقط در ثبت تحویل). |
{
"success": false,
"error": {
"code": "ORDER_NOT_SHIPPED_YET",
"message": "برای تحویل سفارش ابتدا باید سفارش ارسال شده باشد.",
"details": null
}
}اصلاح کاهشی سفارش
/gateway/order/{order_id}/update| کد خطا | HTTP | توضیح و اقدام پیشنهادی |
|---|---|---|
| VALIDATION_ERROR | 422 | ساختار request نامعتبر است؛ برای مثال ارسال همزمان items و shipments، id تکراری برای مرسولهها، مقدار منفی، یا external_version_id بیش از ۲۵۵ کاراکتر. |
| ORDER_NOT_FOUND | 404 | سفارشی با این order_id وجود ندارد یا متعلق به این پذیرنده نیست. |
| ORDER_ATTEMPT_NOT_PAID | 400 | سفارش هنوز تأیید نشده است؛ اصلاح فقط برای سفارش تأییدشده ممکن است. |
| ORDER_UPDATE_VALUE_INCREASED | 400 | اصلاح باعث افزایش مقدار یا ارزش سفارش شده است؛ یا مرسوله یا قلمی آمده که در سفارش نیست، قلمی بدون ID کالا آمده، یا یک ترکیب (کالا + تنوع) تکرار شده است. |
| ORDER_ITEM_PRICE_MISMATCH | 400 | قیمت واحد ارسالی با قیمت ثبتشده روی سفارش یکی نیست. |
| INVALID_ORDER_AMOUNT | 400 | تخفیف ارسالی از مجموع اقلام و خدمات باقیمانده بیشتر است و مبلغ جدید منفی میشود. |
| ORDER_UPDATE_NO_CHANGE | 400 | اصلاح ارسالی با وضعیت فعلی سفارش تفاوتی ندارد. |
| DUPLICATE_EXTERNAL_VERSION_ID | 409 | external_version_id قبلاً برای یکی از سفارشهای همین پذیرنده ثبت شده است؛ برای هر اصلاح باید ID جدیدی ارسال شود. |
| ORDER_REFUND_ALLOCATION_FAILED | 409 | مبلغ بازگشتی اصلاح بین حسابهای پرداخت کاربر قابل تقسیم نیست؛ پیش از تلاش مجدد باید با مهراد هماهنگ شود. |
| ORDER_PAYMENT_SOURCE_NOT_FOUND | 404 | سوابق پرداخت سفارش برای بازگشت وجه یافت نشد؛ باید با مهراد هماهنگ شود. |
| MERCHANT_ACCOUNT_NOT_FOUND | 404 | حساب پذیرنده برای این سرویس تعریف نشده است؛ باید با مهراد هماهنگ شود. |
{
"success": false,
"error": {
"code": "ORDER_UPDATE_VALUE_INCREASED",
"message": "در تغییر سفارش، مقادیر فقط میتوانند کاهش پیدا کنند.",
"details": null
}
}لغو سفارش
/gateway/order/{order_id}/cancel| کد خطا | HTTP | توضیح و اقدام پیشنهادی |
|---|---|---|
| VALIDATION_ERROR | 422 | order_id قالب UUID ندارد. |
| ORDER_NOT_FOUND | 404 | سفارشی با این order_id وجود ندارد. |
| ORDER_MERCHANT_NOT_MATCHED | 400 | سفارش متعلق به پذیرندهٔ دیگری است. |
| ORDER_ATTEMPT_NOT_PAID | 400 | سفارش هنوز تأیید نشده است؛ پرداخت تأییدنشده پس از پایان مهلت خودکار به کاربر بازمیگردد. |
| ORDER_UPDATE_NO_CHANGE | 400 | تمام مبلغ سفارش پیشتر بازگردانده شده است؛ لغو دوباره لازم نیست. |
| ORDER_REFUND_ALLOCATION_FAILED | 409 | مبلغ بازگشتی بین حسابهای پرداخت کاربر قابل تقسیم نیست؛ پیش از تلاش مجدد باید با مهراد هماهنگ شود. |
| ORDER_PAYMENT_SOURCE_NOT_FOUND | 404 | سوابق پرداخت سفارش برای بازگشت وجه یافت نشد؛ باید با مهراد هماهنگ شود. |
| MERCHANT_ACCOUNT_NOT_FOUND | 404 | حساب پذیرنده برای این سرویس تعریف نشده است؛ باید با مهراد هماهنگ شود. |
{
"success": false,
"error": {
"code": "ORDER_UPDATE_NO_CHANGE",
"message": "وضعیت جدید اعلامشده برای سفارش با وضعیت فعلی آن تفاوتی ندارد.",
"details": null
}
}جزئیات سفارش
/gateway/order/{order_id}| کد خطا | HTTP | توضیح و اقدام پیشنهادی |
|---|---|---|
| VALIDATION_ERROR | 422 | order_id قالب UUID ندارد. |
| ORDER_NOT_FOUND | 404 | سفارشی با این order_id وجود ندارد یا متعلق به این پذیرنده نیست. |
{
"success": false,
"error": {
"code": "ORDER_NOT_FOUND",
"message": "سفارشی با این مشخصات وجود ندارد.",
"details": null
}
}چکلیست پیادهسازی
- دریافت Access Token از SSO و اطمینان از فعال بودن scope
order - تولید
external_order_idیکتا برای هر سفارش - ثبت اقلام با
shipments.itemsمنسوخ است و در نسخههای آینده حذف میشود - محاسبهٔ
amountطبق قاعدهٔ مبلغ (اقلام،shipping_feeمرسولهها،servicesوdiscount_amount) - ذخیرهٔ
authority،order_idوtracking_codeکنار سفارش - هدایت کاربر به
payment_urlبدون هیچ تغییری در آدرس - پیادهسازی مسیر
callback_urlو خواندنauthorityوstatus - استعلام وضعیت از API و اتکا نکردن به parameterهای آدرس بازگشت
- تأیید پرداخت با
authorityو مبلغ دقیق، داخل مهلت ۳۰ دقیقه - ذخیرهٔ
ref_idبرای تطبیق حساب - مدیریت حالتی که کاربر هرگز به سایت بازنمیگردد
- مدیریت خطاهای
ORDER_ALREADY_PAID،ORDER_ATTEMPT_NOT_PAIDوORDER_AMOUNT_NOT_MATCHED - پیادهسازی ارسال، تحویل (بهازای هر مرسوله و با
shipment_id)، اصلاح و لغو سفارش در صورت نیاز کسبوکار - تولید
external_version_idیکتا برای هر اصلاح سفارش