{
  "info": {
    "_postman_id": "8ad2e0c0-c692-4a4f-b971-8eab536ef031",
    "name": "TenkiPay Merchant API",
    "description": "Server-to-server merchant integration for hosted checkout, invoices, TenkiPass, and business operations. Replace the collection credentials before sending requests. The pre-request script uses Web Crypto to sign the exact resolved request path and raw JSON body with HMAC-SHA256 v2. Never export a collection containing a live secret.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "const publicKey = pm.collectionVariables.get('public_key');",
          "const secretKey = pm.collectionVariables.get('secret_key');",
          "if (!publicKey || !secretKey || publicKey.includes('REPLACE_ME') || secretKey.includes('REPLACE_ME')) {",
          "  throw new Error('Set public_key and secret_key in the collection variables before sending requests.');",
          "}",
          "const timestamp = new Date().toISOString();",
          "const method = pm.request.method.toUpperCase();",
          "const path = pm.variables.replaceIn(pm.request.url.getPath());",
          "const body = pm.request.body && pm.request.body.mode === 'raw'",
          "  ? pm.variables.replaceIn(pm.request.body.raw || '')",
          "  : '';",
          "const canonical = [timestamp, method, path, body].join('\\n');",
          "const encoder = new TextEncoder();",
          "const hmacKey = await crypto.subtle.importKey(",
          "  'raw', encoder.encode(secretKey), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']",
          ");",
          "const signatureBytes = await crypto.subtle.sign('HMAC', hmacKey, encoder.encode(canonical));",
          "const signature = Array.from(new Uint8Array(signatureBytes))",
          "  .map(byte => byte.toString(16).padStart(2, '0'))",
          "  .join('');",
          "pm.request.headers.upsert({ key: 'Accept', value: 'application/json' });",
          "pm.request.headers.upsert({ key: 'X-TenkiPay-Key', value: publicKey });",
          "pm.request.headers.upsert({ key: 'X-TenkiPay-Timestamp', value: timestamp });",
          "pm.request.headers.upsert({ key: 'X-TenkiPay-Signature', value: signature });"
        ]
      }
    }
  ],
  "variable": [
    { "key": "base_url", "value": "https://me.tenkipay.com", "type": "string" },
    { "key": "public_key", "value": "pk_test_REPLACE_ME", "type": "string" },
    { "key": "secret_key", "value": "sk_test_REPLACE_ME", "type": "secret" },
    { "key": "session_id", "value": "CS-REPLACE_ME", "type": "string" },
    { "key": "checkout_url", "value": "", "type": "string" },
    { "key": "invoice_number", "value": "INV-REPLACE_ME", "type": "string" },
    { "key": "entitlement_token", "value": "TICKET-REPLACE_ME", "type": "string" },
    { "key": "workspace_id", "value": "00000000-0000-0000-0000-000000000000", "type": "string" },
    { "key": "item_id", "value": "00000000-0000-0000-0000-000000000000", "type": "string" },
    { "key": "sale_id", "value": "00000000-0000-0000-0000-000000000000", "type": "string" },
    { "key": "room_id", "value": "00000000-0000-0000-0000-000000000000", "type": "string" },
    { "key": "stay_id", "value": "00000000-0000-0000-0000-000000000000", "type": "string" }
  ],
  "item": [
    {
      "name": "TenkiPay Checkout",
      "description": "Create a fixed-amount hosted checkout on your server, then redirect the customer browser to data.checkout_url.",
      "item": [
        {
          "name": "Create checkout session",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "checkout-order-1001-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"merchant_reference\": \"ORDER-1001\",\n  \"amount\": \"250.00\",\n  \"currency\": \"SLE\",\n  \"description\": \"Online store order 1001\",\n  \"success_url\": \"https://shop.example.com/orders/1001/paid\",\n  \"cancel_url\": \"https://shop.example.com/orders/1001\",\n  \"customer_name\": \"Aminata Kamara\",\n  \"customer_phone\": \"+23276000000\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/checkout/sessions",
            "description": "Requires checkout.sessions:write. Keep the Idempotency-Key stable when retrying this exact order attempt."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Checkout session created', () => pm.expect(pm.response.code).to.be.oneOf([200, 201]));",
                  "const payload = pm.response.json();",
                  "if (payload.data && payload.data.session_id) pm.collectionVariables.set('session_id', payload.data.session_id);",
                  "if (payload.data && payload.data.checkout_url) pm.collectionVariables.set('checkout_url', payload.data.checkout_url);"
                ]
              }
            }
          ]
        },
        {
          "name": "Retrieve checkout session",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{base_url}}/api/v1/merchant/checkout/sessions/{{session_id}}",
            "description": "Requires checkout.sessions:read. Treat only status ACSC as paid."
          }
        }
      ]
    },
    {
      "name": "Invoices",
      "item": [
        {
          "name": "Create invoice",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "invoice-erp-1042-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"external_reference\": \"INV-ERP-1042\",\n  \"customer_name\": \"Aminata Kamara\",\n  \"customer_email\": \"aminata@example.com\",\n  \"description\": \"August services\",\n  \"currency\": \"SLE\",\n  \"allow_partial_payments\": false,\n  \"publish\": true,\n  \"items\": [\n    { \"description\": \"Professional services\", \"quantity\": 1, \"unit_amount\": \"850.00\" }\n  ]\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/invoices",
            "description": "Requires invoices:write."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Invoice created', () => pm.expect(pm.response.code).to.be.oneOf([200, 201]));",
                  "const payload = pm.response.json();",
                  "if (payload.data && payload.data.invoice_number) pm.collectionVariables.set('invoice_number', payload.data.invoice_number);"
                ]
              }
            }
          ]
        },
        {
          "name": "Retrieve invoice",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{base_url}}/api/v1/merchant/invoices/{{invoice_number}}",
            "description": "Requires invoices:read. The path accepts invoice number, collection reference, or external reference."
          }
        }
      ]
    },
    {
      "name": "TenkiPass",
      "description": "Live-approved credentials are required for entitlement verification and redemption.",
      "item": [
        {
          "name": "Verify entitlement",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"{{entitlement_token}}\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/entitlements/verify"
          }
        },
        {
          "name": "Redeem entitlement",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "redeem-entitlement-1001-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"{{entitlement_token}}\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/entitlements/redeem"
          }
        }
      ]
    },
    {
      "name": "TenkiOps Business Operations",
      "description": "Tenant- and environment-isolated APIs for hotel, retail, fuel, and pharmacy operations.",
      "item": [
        {
          "name": "List workspaces",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces"
          }
        },
        {
          "name": "Create workspace",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "workspace-demo-hotel-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Demo Hotel\",\n  \"operation_type\": \"HOTEL\",\n  \"currency\": \"SLE\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const payload = pm.response.json();",
                  "const id = payload.data && (payload.data.id || payload.data.workspace_id);",
                  "if (id) pm.collectionVariables.set('workspace_id', id);"
                ]
              }
            }
          ]
        },
        {
          "name": "List catalog",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces/{{workspace_id}}/catalog"
          }
        },
        {
          "name": "Create catalog item",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "catalog-room-service-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"sku\": \"ROOM-SERVICE-001\",\n  \"name\": \"Room service breakfast\",\n  \"category\": \"Food\",\n  \"item_type\": \"FOOD\",\n  \"unit\": \"UNIT\",\n  \"sale_price\": \"75.00\",\n  \"track_inventory\": false,\n  \"opening_stock\": \"0\",\n  \"reorder_level\": \"0\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces/{{workspace_id}}/catalog"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const payload = pm.response.json();",
                  "const id = payload.data && (payload.data.id || payload.data.item_id);",
                  "if (id) pm.collectionVariables.set('item_id', id);"
                ]
              }
            }
          ]
        },
        {
          "name": "Adjust stock",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "stock-receipt-1001-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"direction\": \"IN\",\n  \"quantity\": \"20\",\n  \"unit_cost\": \"40.00\",\n  \"note\": \"Opening delivery\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces/{{workspace_id}}/catalog/{{item_id}}/stock-adjustments"
          }
        },
        {
          "name": "List sales",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces/{{workspace_id}}/sales"
          }
        },
        {
          "name": "Create sale",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "sale-demo-1001-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"customer_name\": \"Aminata Kamara\",\n  \"customer_phone\": \"+23276000000\",\n  \"tax_amount\": \"0.00\",\n  \"discount_amount\": \"0.00\",\n  \"items\": [\n    { \"catalog_item_id\": \"{{item_id}}\", \"quantity\": \"1\" }\n  ]\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces/{{workspace_id}}/sales"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const payload = pm.response.json();",
                  "const id = payload.data && (payload.data.id || payload.data.sale_id);",
                  "if (id) pm.collectionVariables.set('sale_id', id);"
                ]
              }
            }
          ]
        },
        {
          "name": "Retrieve sale",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{base_url}}/api/v1/merchant/operations/sales/{{sale_id}}"
          }
        },
        {
          "name": "Create sale checkout",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "sale-checkout-1001-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"success_url\": \"https://shop.example.com/sales/1001/paid\",\n  \"cancel_url\": \"https://shop.example.com/sales/1001\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/operations/sales/{{sale_id}}/checkout"
          }
        },
        {
          "name": "List hotel rooms",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces/{{workspace_id}}/hotel/rooms"
          }
        },
        {
          "name": "Create hotel room",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "hotel-room-101-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"room_number\": \"101\",\n  \"room_type\": \"Standard\",\n  \"nightly_rate\": \"450.00\",\n  \"capacity\": 2,\n  \"amenities\": [\"Wi-Fi\", \"Air conditioning\"]\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces/{{workspace_id}}/hotel/rooms"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const payload = pm.response.json();",
                  "const id = payload.data && (payload.data.id || payload.data.room_id);",
                  "if (id) pm.collectionVariables.set('room_id', id);"
                ]
              }
            }
          ]
        },
        {
          "name": "List hotel stays",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces/{{workspace_id}}/hotel/stays"
          }
        },
        {
          "name": "Check in hotel guest",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "hotel-stay-1001-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"room_id\": \"{{room_id}}\",\n  \"guest_name\": \"Aminata Kamara\",\n  \"guest_phone\": \"+23276000000\",\n  \"adults\": 1,\n  \"children\": 0,\n  \"check_in_at\": \"2026-08-05T14:00:00Z\",\n  \"check_out_at\": \"2026-08-07T10:00:00Z\",\n  \"payment_timing\": \"CHECK_OUT\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/operations/workspaces/{{workspace_id}}/hotel/stays"
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const payload = pm.response.json();",
                  "const id = payload.data && (payload.data.id || payload.data.stay_id);",
                  "if (id) pm.collectionVariables.set('stay_id', id);"
                ]
              }
            }
          ]
        },
        {
          "name": "Add hotel folio charge",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "hotel-charge-breakfast-1001-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"catalog_item_id\": \"{{item_id}}\",\n  \"description\": \"Room service breakfast\",\n  \"quantity\": \"1\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/operations/hotel/stays/{{stay_id}}/charges"
          }
        },
        {
          "name": "Check out hotel guest",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "Idempotency-Key", "value": "hotel-checkout-1001-v1" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": { "raw": { "language": "json" } }
            },
            "url": "{{base_url}}/api/v1/merchant/operations/hotel/stays/{{stay_id}}/checkout"
          }
        }
      ]
    }
  ]
}
