{
	"info": {
		"_postman_id": "b7c4d2e1-9a8f-4e3b-8c5d-1f2a3b4c5d6e",
		"name": "Sepal Payment - Sandbox API (v1)",
		"description": "کالکشن سندباکس — همان ۶ مرحله و همان pathهای پروداکشن، فقط base_url فرق دارد.\n\nبرای رفتن به پروداکشن فقط `base_url` را عوض کنید:\n- سندباکس: `https://payment.sepal.ir/api/v1/sandbox`\n- پروداکشن: `https://payment.sepal.ir/api/v1`\n\nترتیب اجرا:\n1. ایجاد درخواست پرداخت → `paymentNumber` ذخیره می‌شود\n2. صفحه درگاه آزمایشی (HTML) — دکمه پرداخت موفق/ناموفق\n3. Callback داخلی سندباکس → هدایت به callbackUrl پذیرنده\n4. Verify از سرور پذیرنده\n5. GetState\n6. Inquiry\n\nدر سندباکس بانک واقعی صدا زده نمی‌شود. هر `apiKey` غیرخالی پذیرفته می‌شود و تراکنش‌ها از پروداکشن جدا هستند.\n\nمتغیرها: `base_url`, `apiKey`, `callbackUrl`",
		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
	},
	"item": [
		{
			"name": "1. Create Payment Request",
			"event": [
				{
					"listen": "test",
					"script": {
						"type": "text/javascript",
						"exec": [
							"pm.test('status code 200', function () {",
							"    pm.response.to.have.status(200);",
							"});",
							"",
							"var json = {};",
							"try { json = pm.response.json(); } catch (e) {}",
							"",
							"pm.test('status true and paymentNumber', function () {",
							"    pm.expect(json).to.have.property('status', true);",
							"    pm.expect(json).to.have.property('paymentNumber');",
							"});",
							"",
							"if (json && json.paymentNumber) {",
							"    pm.collectionVariables.set('paymentNumber', String(json.paymentNumber));",
							"    console.log('paymentNumber =', json.paymentNumber);",
							"}"
						]
					}
				},
				{
					"listen": "prerequest",
					"script": {
						"type": "text/javascript",
						"exec": [
							"var inv = 'INV-' + Date.now();",
							"pm.collectionVariables.set('invoiceNumber', inv);"
						]
					}
				}
			],
			"request": {
				"method": "POST",
				"header": [
					{ "key": "Content-Type", "value": "application/json" }
				],
				"body": {
					"mode": "raw",
					"raw": "{\n    \"apiKey\": \"{{apiKey}}\",\n    \"amount\": 100000,\n    \"callbackUrl\": \"{{callbackUrl}}\",\n    \"invoiceNumber\": \"{{invoiceNumber}}\",\n    \"callbackMethod\": \"post\",\n    \"payerName\": \"نمونه پرداخت‌کننده\",\n    \"payerMobile\": \"09120000000\",\n    \"payerEmail\": \"payer@example.com\",\n    \"description\": \"تراکنش نمونه\"\n}"
				},
				"url": {
					"raw": "{{base_url}}/payment/request/",
					"host": ["{{base_url}}"],
					"path": ["payment", "request", ""]
				},
				"description": "ایجاد تراکنش پرداخت و دریافت `paymentNumber`.\n\n**اجباری:** `apiKey`, `amount`, `callbackUrl`, `invoiceNumber`\n\n**اختیاری:** `callbackMethod` (پیش‌فرض post)، `payerName`, `payerMobile` (09xxxxxxxxx)، `payerEmail`, `description`, `freezeCardNumber`, `freezeCardNumberList`, `affiliateCode`\n\n**مبلغ:** نباید کمتر از `PAYMENT_MIN_AMOUNT` باشد (پیش‌فرض پیکربندی: ۲۰۰۰۰ ریال). نمونه کالکشن از ۱۰۰۰۰۰ ریال استفاده می‌کند.\n\n**پاسخ موفق:** `status`, `message`, `paymentNumber`\n\n**معادل legacy:** `POST /api/request.json`"
			}
		},
		{
			"name": "2. Process Payment (Get Token + Gateway HTML)",
			"event": [
				{
					"listen": "test",
					"script": {
						"type": "text/javascript",
						"exec": [
							"pm.test('status code 200', function () {",
							"    pm.response.to.have.status(200);",
							"});",
							"",
							"pm.test('returns HTML gateway form', function () {",
							"    var ct = pm.response.headers.get('Content-Type') || '';",
							"    pm.expect(ct).to.include('text/html');",
							"});"
						]
					}
				}
			],
			"request": {
				"method": "GET",
				"header": [],
				"url": {
					"raw": "{{base_url}}/payment/{{paymentNumber}}/",
					"host": ["{{base_url}}"],
					"path": ["payment", "{{paymentNumber}}", ""]
				},
				"description": "دریافت توکن درگاه و بازگرداندن HTML با فرم auto-submit به بانک.\n\n**پارامتر مسیر:** `paymentNumber`\n\n**متد:** GET و POST پشتیبانی می‌شوند.\n\n**پاسخ موفق:** `Content-Type: text/html`\n**پاسخ خطا:** JSON با `status`, `message`, `paymentNumber`\n\nپذیرنده فقط مرورگر پرداخت‌کننده را به این URL هدایت می‌کند؛ ساخت فرم درگاه لازم نیست.\n\n**معادل legacy:** `GET|POST /api/payment/{paymentNumber}/`"
			}
		},
		{
			"name": "3. Payment Callback (Bank only — do not call as merchant)",
			"request": {
				"method": "POST",
				"header": [
					{ "key": "Content-Type", "value": "application/x-www-form-urlencoded" }
				],
				"body": {
					"mode": "urlencoded",
					"urlencoded": [
						{ "key": "RefNum", "value": "{{token}}", "type": "text" },
						{ "key": "ResNum", "value": "{{paymentNumber}}", "type": "text" },
						{ "key": "respcode", "value": "0", "type": "text" },
						{ "key": "TraceNo", "value": "123456", "type": "text" }
					]
				},
				"url": {
					"raw": "{{base_url}}/payment/{{paymentNumber}}/callback/",
					"host": ["{{base_url}}"],
					"path": ["payment", "{{paymentNumber}}", "callback", ""]
				},
				"description": "**پذیرنده این endpoint را فراخوانی نمی‌کند.** فقط برای شبیه‌سازی بازگشت بانک در محیط تست است.\n\nدر پروداکشن بانک به سپال می‌زند؛ سپس سپال پرداخت‌کننده را به `callbackUrl` پذیرنده هدایت می‌کند.\n\n**پارامترهای ارسالی به callbackUrl پذیرنده:**\n- همیشه: `status` (۱=موفق / ۰=ناموفق)، `paymentNumber`, `invoiceNumber`\n- وقتی status=1: `amount`, `cardNumber`, `referenceNumber`\n\nروش ارسال مطابق `callbackMethod` است (get = query string، post = فرم HTML).\n\n`status=1` به‌معنای تأیید قطعی نیست؛ مرحلهٔ بعد Verify است.\n\n**معادل legacy:** `GET|POST /api/payment/{paymentNumber}/callback/`"
			}
		},
		{
			"name": "4. Verify Payment",
			"event": [
				{
					"listen": "test",
					"script": {
						"type": "text/javascript",
						"exec": [
							"var json = {};",
							"try { json = pm.response.json(); } catch (e) {}",
							"",
							"pm.test('success response shape', function () {",
							"    if (pm.response.code === 200) {",
							"        pm.expect(json).to.have.property('status', true);",
							"        pm.expect(json).to.have.property('alreadyVerified');",
							"        pm.expect(json).to.have.property('referenceNumber');",
							"        pm.expect(json).to.have.property('amount');",
							"    }",
							"});",
							"",
							"if (json && typeof json.alreadyVerified !== 'undefined') {",
							"    console.log('alreadyVerified =', json.alreadyVerified);",
							"}"
						]
					}
				}
			],
			"request": {
				"method": "POST",
				"header": [
					{ "key": "Content-Type", "value": "application/json" }
				],
				"body": {
					"mode": "raw",
					"raw": "{\n    \"apiKey\": \"{{apiKey}}\",\n    \"paymentNumber\": \"{{paymentNumber}}\",\n    \"invoiceNumber\": \"{{invoiceNumber}}\"\n}"
				},
				"url": {
					"raw": "{{base_url}}/payment/verify/",
					"host": ["{{base_url}}"],
					"path": ["payment", "verify", ""]
				},
				"description": "تأیید قطعی تراکنش توسط پذیرنده. باید از **سرور پذیرنده** فراخوانی شود (نه مرورگر).\n\n**اجباری:** `apiKey`, `paymentNumber`\n**اختیاری:** `invoiceNumber` — در صورت ارسال باید با فاکتور ثبت‌شده مطابقت داشته باشد\n\n**پاسخ موفق:**\n- `status` (true)\n- `message`\n- `referenceNumber`, `cardNumber`, `amount`\n- `alreadyVerified`: `false` = تأیید همین درخواست؛ `true` = قبلاً تأیید شده (idempotent، بدون تأیید مجدد نزد درگاه)\n\nتماس مجدد پس از موفقیت مجاز است؛ `status` همچنان true می‌ماند.\n\n**معادل legacy:** `POST /api/verify.json`"
			}
		},
		{
			"name": "5. Get Payment State",
			"event": [
				{
					"listen": "test",
					"script": {
						"type": "text/javascript",
						"exec": [
							"var json = {};",
							"try { json = pm.response.json(); } catch (e) {}",
							"",
							"pm.test('item shape on success', function () {",
							"    if (pm.response.code === 200 && json.status) {",
							"        pm.expect(json).to.have.property('item');",
							"        pm.expect(json.item).to.have.property('paymentNumber');",
							"        pm.expect(json.item).to.have.property('state');",
							"        pm.expect(json.item).to.have.property('stateLabel');",
							"    }",
							"});"
						]
					}
				}
			],
			"request": {
				"method": "POST",
				"header": [
					{ "key": "Content-Type", "value": "application/json" }
				],
				"body": {
					"mode": "raw",
					"raw": "{\n    \"apiKey\": \"{{apiKey}}\",\n    \"paymentNumber\": \"{{paymentNumber}}\"\n}"
				},
				"url": {
					"raw": "{{base_url}}/payment/state/",
					"host": ["{{base_url}}"],
					"path": ["payment", "state", ""]
				},
				"description": "دریافت وضعیت فعلی یک تراکنش. جایگزین Verify نیست و وضعیت را نزد درگاه قطعی نمی‌کند.\n\n**اجباری:** `apiKey`, `paymentNumber`\n\n**پاسخ موفق:** `status`, `message`, `item` شامل:\n- `paymentNumber`\n- `state`\n- `stateLabel`\n- `payerCardNumber`\n\n**معادل legacy:** `POST /api/getState.json`"
			}
		},
		{
			"name": "6. Inquiry Payments",
			"event": [
				{
					"listen": "test",
					"script": {
						"type": "text/javascript",
						"exec": [
							"var json = {};",
							"try { json = pm.response.json(); } catch (e) {}",
							"",
							"pm.test('list shape on success', function () {",
							"    if (pm.response.code === 200 && json.status) {",
							"        pm.expect(json).to.have.property('items');",
							"        pm.expect(json).to.have.property('page');",
							"        pm.expect(json).to.have.property('limit');",
							"        pm.expect(json).to.have.property('totalPages');",
							"    }",
							"});"
						]
					}
				}
			],
			"request": {
				"method": "POST",
				"header": [
					{ "key": "Content-Type", "value": "application/json" }
				],
				"body": {
					"mode": "raw",
					"raw": "{\n    \"apiKey\": \"{{apiKey}}\",\n    \"dateFrom\": \"{{dateFrom}}\",\n    \"dateTo\": \"{{dateTo}}\",\n    \"invoiceNumber\": \"{{invoiceNumber}}\",\n    \"page\": 1,\n    \"limit\": 20\n}"
				},
				"url": {
					"raw": "{{base_url}}/payment/inquiry/",
					"host": ["{{base_url}}"],
					"path": ["payment", "inquiry", ""]
				},
				"description": "جستجو و صفحه‌بندی فهرست تراکنش‌های درگاه.\n\n**اجباری:** `apiKey` به‌همراه **حداقل یک** فیلتر از:\n`dateFrom`, `dateTo`, `payerCardNumber`, `payerEmail`, `invoiceNumber`\n\nتوجه: `state` به‌تنهایی فیلتر جستجوی الزامی محسوب نمی‌شود.\n\n**اختیاری:** `state`, `page` (پیش‌فرض ۱)، `limit` (پیش‌فرض ۲۰، حداکثر ۱۰۰)\n\nاگر `dateFrom` و `dateTo` هر دو ارسال شوند، بازه نباید از ۳۱ روز بیشتر باشد.\n\n**پاسخ موفق:** `status`, `message`, `items` (فیلدهای داخل آیتم snake_case)، `page`, `limit`, `totalPages`\n\nنمونه کالکشن با `dateFrom`/`dateTo` و `invoiceNumber` حداقل فیلتر را تأمین می‌کند.\n\n**معادل legacy:** `POST /api/inquiry.json`"
			}
		}
	],
	"variable": [
		{ "key": "base_url", "value": "https://payment.sepal.ir/api/v1/sandbox", "type": "string" },
		{ "key": "apiKey", "value": "SANDBOX_API_KEY", "type": "string" },
		{ "key": "callbackUrl", "value": "https://merchant.example.com/callback", "type": "string" },
		{ "key": "paymentNumber", "value": "", "type": "string" },
		{ "key": "invoiceNumber", "value": "", "type": "string" },
		{ "key": "token", "value": "", "type": "string" },
		{ "key": "dateFrom", "value": "2026-07-01", "type": "string" },
		{ "key": "dateTo", "value": "2026-07-25", "type": "string" }
	]
}
