{
	"info": {
		"_postman_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
		"name": "Vntix API v1",
		"description": "Bearer partner API. Sửa biến collection base_url (vd http://localhost hoặc https://ten-mien). Chạy GET products hoặc GET get_packages=1 trước để tự điền product_id / package_id / price_rule_id; sau POST booking sẽ lưu booking_code. Không commit token lên kho công khai.",
		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
	},
	"auth": {
		"type": "bearer",
		"bearer": [
			{
				"key": "token",
				"value": "{{api_token}}",
				"type": "string"
			}
		]
	},
	"variable": [
		{
			"key": "base_url",
			"value": "http://localhost"
		},
		{
			"key": "api_token",
			"value": "vntix_3096866390beb61188c88bb270576e19ec706429"
		},
		{
			"key": "product_id",
			"value": "1"
		},
		{
			"key": "package_id",
			"value": "1"
		},
		{
			"key": "price_rule_id",
			"value": "17"
		},
		{
			"key": "visit_date",
			"value": "2026-04-15"
		},
		{
			"key": "booking_code",
			"value": ""
		}
	],
	"item": [
		{
			"name": "1. Products",
			"item": [
				{
					"name": "GET /api/v1/products",
					"request": {
						"method": "GET",
						"header": [],
						"url": "{{base_url}}/api/v1/products?page=1&page_size=10&date={{visit_date}}"
					},
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"try {",
									"  const j = pm.response.json();",
									"  if (j.success && Array.isArray(j.data) && j.data.length && j.data[0].id) {",
									"    pm.collectionVariables.set('product_id', String(j.data[0].id));",
									"  }",
									"} catch (e) {}"
								],
								"type": "text/javascript"
							}
						}
					]
				},
				{
					"name": "GET /api/v1/products (get_packages=1)",
					"request": {
						"method": "GET",
						"header": [],
						"url": "{{base_url}}/api/v1/products?get_packages=1&page=1&page_size=5&date={{visit_date}}"
					},
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"try {",
									"  const j = pm.response.json();",
									"  if (j.success && Array.isArray(j.data) && j.data.length) {",
									"    const p = j.data[0];",
									"    if (p.id) pm.collectionVariables.set('product_id', String(p.id));",
									"    if (p.packages && p.packages.length) {",
									"      const pkg = p.packages[0];",
									"      if (pkg.id) pm.collectionVariables.set('package_id', String(pkg.id));",
									"      const pr = pkg.prices && pkg.prices[0];",
									"      const rules = pr && pr.price_rules;",
									"      if (rules && rules.length && rules[0].price_rule_id) {",
									"        pm.collectionVariables.set('price_rule_id', String(rules[0].price_rule_id));",
									"      }",
									"    }",
									"  }",
									"} catch (e) {}"
								],
								"type": "text/javascript"
							}
						}
					]
				},
				{
					"name": "GET /api/v1/products/{id}/packages",
					"request": {
						"method": "GET",
						"header": [],
						"url": "{{base_url}}/api/v1/products/{{product_id}}/packages"
					},
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"try {",
									"  const j = pm.response.json();",
									"  if (j.success && j.data && j.data.packages && j.data.packages.length) {",
									"    const pkg = j.data.packages[0];",
									"    if (pkg.id) pm.collectionVariables.set('package_id', String(pkg.id));",
									"    const pr = pkg.prices && pkg.prices[0];",
									"    const rules = pr && pr.price_rules;",
									"    if (rules && rules.length && rules[0].price_rule_id) {",
									"      pm.collectionVariables.set('price_rule_id', String(rules[0].price_rule_id));",
									"    }",
									"  }",
									"} catch (e) {}"
								],
								"type": "text/javascript"
							}
						}
					]
				},
				{
					"name": "GET /api/v1/products/{id}",
					"request": {
						"method": "GET",
						"header": [],
						"url": "{{base_url}}/api/v1/products/{{product_id}}"
					},
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"try {",
									"  const j = pm.response.json();",
									"  pm.test('Status 200', function () { pm.response.to.have.status(200); });",
									"  if (j.success && j.data && j.data.id) {",
									"    pm.collectionVariables.set('product_id', String(j.data.id));",
									"  }",
									"  if (j.success && j.data && j.data.packages && j.data.packages.length) {",
									"    const pkg = j.data.packages[0];",
									"    if (pkg.id) pm.collectionVariables.set('package_id', String(pkg.id));",
									"    const pr = pkg.prices && pkg.prices[0];",
									"    const rules = pr && pr.price_rules;",
									"    if (rules && rules.length && rules[0].price_rule_id) {",
									"      pm.collectionVariables.set('price_rule_id', String(rules[0].price_rule_id));",
									"    }",
									"  }",
									"} catch (e) {}"
								],
								"type": "text/javascript"
							}
						}
					]
				}
			]
		},
		{
			"name": "2. Bookings",
			"item": [
				{
					"name": "POST /api/v1/booking/create",
					"request": {
						"method": "POST",
						"header": [
							{
								"key": "Content-Type",
								"value": "application/json"
							}
						],
						"body": {
							"mode": "raw",
							"raw": "{\n  \"product_id\": {{product_id}},\n  \"package_id\": {{package_id}},\n  \"date\": \"{{visit_date}}\",\n  \"lines\": [\n    { \"price_rule_id\": {{price_rule_id}}, \"quantity\": 1 }\n  ],\n  \"contact\": {\n    \"full_name\": \"Test Postman\",\n    \"tel\": \"0901234567\"\n  },\n  \"idempotency_key\": \"postman-{{$guid}}\"\n}"
						},
						"url": "{{base_url}}/api/v1/booking/create"
					},
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"pm.test('Status 201 or 200', function () {",
									"  pm.expect([200, 201]).to.include(pm.response.code);",
									"});",
									"try {",
									"  const j = pm.response.json();",
									"  if (j.success && j.data && j.data.code) {",
									"    pm.collectionVariables.set('booking_code', j.data.code);",
									"    pm.test('Has booking code', function () { pm.expect(j.data.code).to.be.a('string'); });",
									"  }",
									"} catch (e) { pm.test('JSON body', function () { pm.expect.fail(e.message); }); }"
								],
								"type": "text/javascript"
							}
						}
					]
				},
				{
					"name": "GET /api/v1/bookings/{code}",
					"request": {
						"method": "GET",
						"header": [],
						"url": "{{base_url}}/api/v1/bookings/{{booking_code}}"
					},
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"pm.test('Status 200', function () { pm.response.to.have.status(200); });",
									"try {",
									"  const j = pm.response.json();",
									"  pm.test('success', function () { pm.expect(j.success).to.be.true; });",
									"} catch (e) {}"
								],
								"type": "text/javascript"
							}
						}
					]
				},
				{
					"name": "POST /api/v1/bookings/{code}/issue",
					"request": {
						"method": "POST",
						"header": [],
						"body": {
							"mode": "raw",
							"raw": ""
						},
						"url": "{{base_url}}/api/v1/bookings/{{booking_code}}/issue"
					},
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"try {",
									"  const j = pm.response.json();",
									"  if (j.success) { pm.test('Issue response ok', function () { pm.expect(j.success).to.be.true; }); }",
									"} catch (e) {}"
								],
								"type": "text/javascript"
							}
						}
					]
				}
			]
		},
		{
			"name": "3. Wallet",
			"item": [
				{
					"name": "GET /api/v1/wallet/balance",
					"request": {
						"method": "GET",
						"header": [],
						"url": "{{base_url}}/api/v1/wallet/balance"
					},
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"pm.test('Status 200', function () { pm.response.to.have.status(200); });",
									"try {",
									"  const j = pm.response.json();",
									"  pm.test('success', function () { pm.expect(j.success).to.be.true; });",
									"  pm.test('has wallet balance', function () { pm.expect(j.data).to.have.property('balance'); });",
									"} catch (e) {}"
								],
								"type": "text/javascript"
							}
						}
					]
				}
			]
		},
		{
			"name": "4. Auth negative",
			"item": [
				{
					"name": "GET products — no token",
					"request": {
						"auth": {
							"type": "noauth"
						},
						"method": "GET",
						"header": [],
						"url": "{{base_url}}/api/v1/products"
					},
					"event": [
						{
							"listen": "test",
							"script": {
								"exec": [
									"pm.test('Unauthorized or JSON error', function () {",
									"  const code = pm.response.code;",
									"  let j = {}; try { j = pm.response.json(); } catch (e) {}",
									"  pm.expect(code === 401 || (j && j.success === false)).to.be.true;",
									"});"
								],
								"type": "text/javascript"
							}
						}
					]
				}
			]
		}
	]
}
