{
  "openapi": "3.1.0",
  "info": {
    "title": "PinnPay Shopee Gateway",
    "version": "1.0.0",
    "description": "Gateway QRIS ShopeePay. Semua timestamp WIB (UTC+7) 'YYYY-MM-DD HH:MM:SS'. Tampilkan `nominal` dari respons ke pelanggan (bisa +1..99 dari yang diminta agar unik). Dokumentasi naratif: /docs.md"
  },
  "servers": [{ "url": "https://pinnduit.my.id" }],
  "tags": [{ "name": "payment" }, { "name": "system" }],
  "paths": {
    "/api/pay": {
      "post": {
        "tags": ["payment"],
        "operationId": "createPayment",
        "summary": "Buat transaksi + QRIS dinamis",
        "description": "Jangan ulangi buta-buta saat jaringan gagal; periksa status dengan order_ref unik agar tidak membuat transaksi ganda.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayRequest" },
            "example": { "client_id": "toko_a", "apikey": "API_KEY_CLIENT", "nominal": 50000, "order_ref": "INV-123" } } }
        },
        "responses": {
          "200": { "description": "Transaksi dibuat", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayResponse" } } } },
          "400": { "description": "Payload tidak lengkap / nominal tidak valid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Kredensial tidak valid", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "503": { "description": "Terlalu banyak transaksi aktif dengan nominal ini; coba lagi", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/status/{tx_id}": {
      "get": {
        "tags": ["payment"],
        "operationId": "getPaymentStatus",
        "summary": "Status transaksi milik client",
        "description": "Gunakan jeda polling >= 3 detik. Webhook adalah jalur utama.",
        "parameters": [
          { "name": "tx_id", "in": "path", "required": true, "schema": { "type": "integer" } },
          { "name": "X-Client-Id", "in": "header", "required": true, "schema": { "type": "string" } },
          { "name": "X-Api-Key", "in": "header", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Transaction" } } } },
          "404": { "description": "Tidak ditemukan / bukan milik client", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/health": {
      "get": {
        "tags": ["system"],
        "operationId": "health",
        "summary": "Cek layanan",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": {
          "type": "object", "properties": {
            "status": { "type": "string", "example": "online" }, "service": { "type": "string" },
            "wib_time": { "type": "string" }, "session_valid": { "type": "boolean", "description": "false = sesi Shopee gateway mati; pembayaran belum bisa dicocokkan" } } } } } } }
      }
    }
  },
  "webhooks": {
    "paymentPaid": {
      "post": {
        "summary": "Dikirim gateway ke webhook_url client saat transaksi PAID",
        "description": "Header X-PinnPay-Signature = hex HMAC-SHA256(raw body, apikey). Balas 2xx. Dicoba ulang (0s, 10s, 1m, 5m per putaran; disapu tiap menit; maks 12 percobaan, transaksi < 24 jam). Idempoten berdasarkan tx_id.",
        "parameters": [{ "name": "X-PinnPay-Signature", "in": "header", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookPayload" } } } },
        "responses": { "200": { "description": "Diterima (apa pun status 2xx dianggap sukses)" } }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": { "type": "object", "properties": { "error": { "type": "string" } }, "required": ["error"] },
      "PayRequest": {
        "type": "object", "required": ["client_id", "apikey", "nominal"],
        "properties": {
          "client_id": { "type": "string" },
          "apikey": { "type": "string", "description": "Hanya di server; jangan di klien/browser." },
          "nominal": { "type": "integer", "minimum": 1, "maximum": 50000000, "description": "Rupiah" },
          "order_ref": { "type": "string", "maxLength": 64, "description": "Referensi order Anda; alias order_id" }
        }
      },
      "Transaction": {
        "type": "object",
        "properties": {
          "tx_id": { "type": "integer" }, "client_id": { "type": "string" }, "order_ref": { "type": ["string", "null"] },
          "status": { "type": "string", "enum": ["PENDING", "PAID", "EXPIRED"], "description": "EXPIRED bisa menjadi PAID dalam masa tenggang 30 menit" },
          "nominal": { "type": "integer", "description": "Jumlah yang harus dibayar pelanggan" },
          "base_nominal": { "type": "integer" },
          "created_at": { "type": "string" }, "expired_at": { "type": "string" },
          "paid_at": { "type": ["string", "null"] }, "rrn": { "type": ["string", "null"] }
        }
      },
      "PayResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/Transaction" },
          { "type": "object", "properties": {
              "message": { "type": "string" },
              "unique_add": { "type": "integer", "minimum": 0, "maximum": 99, "description": "nominal = base_nominal + unique_add" },
              "qris_payload": { "type": "string", "description": "Teks QRIS dinamis; ubah menjadi gambar QR di sisi Anda" } } }
        ]
      },
      "WebhookPayload": {
        "type": "object",
        "properties": {
          "status": { "type": "string", "const": "PAID" }, "client_id": { "type": "string" }, "tx_id": { "type": "integer" },
          "order_ref": { "type": ["string", "null"] }, "nominal": { "type": "integer" }, "base_nominal": { "type": "integer" },
          "rrn": { "type": ["string", "null"] }, "provider_txid": { "type": ["string", "null"] },
          "sender": { "type": ["string", "null"] }, "time_paid": { "type": "string" }
        }
      }
    }
  }
}
