{
  "openapi": "3.0.3",
  "info": {
    "title": "PEWANG Gateway M-Pesa Partner API",
    "version": "1.0.0",
    "description": "Integration-grade API for WhatsApp-driven M-Pesa collections, reconciliation, receipts, and signed webhook delivery."
  },
  "servers": [
    {
      "url": "https://gateway.pewang.company",
      "description": "Production"
    }
  ],
  "tags": [
    { "name": "Payments" },
    { "name": "Reconciliation" },
    { "name": "Receipts" },
    { "name": "Webhooks" }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "schema": { "type": "string", "minLength": 8, "maxLength": 128 },
        "description": "Client-generated unique key for safe retries. Reuse the same key only with the exact same payload."
      },
      "CheckoutRequestId": {
        "name": "checkoutRequestId",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      }
    },
    "schemas": {
      "StkPushRequest": {
        "type": "object",
        "required": ["phone", "amountKes"],
        "properties": {
          "phone": { "type": "string", "example": "254712345678" },
          "amountKes": { "type": "number", "example": 1500 },
          "accountReference": { "type": "string", "example": "LN-2026-001" },
          "transactionDesc": { "type": "string", "example": "LoanInstallment" },
          "clientId": { "type": "string", "example": "bank_collection_bot" },
          "whatsappNotifyPhone254": { "type": "string", "example": "254711164069" }
        }
      },
      "StkPushResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "message": { "type": "string", "example": "Check your phone for M-Pesa PIN prompt." },
          "idempotentReplay": { "type": "boolean", "example": false },
          "data": {
            "type": "object",
            "properties": {
              "checkoutRequestId": { "type": "string", "example": "ws_CO_260420261234567890123" },
              "merchantRequestId": { "type": "string", "example": "29115-34620561-1" },
              "amountKes": { "type": "number", "example": 1500 },
              "phone254": { "type": "string", "example": "254712345678" }
            }
          }
        }
      },
      "RedeemReRequest": {
        "type": "object",
        "required": ["token"],
        "properties": {
          "token": { "type": "string", "example": "A1B2C3D4" }
        }
      },
      "WebhookConfigRequest": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url": { "type": "string", "format": "uri", "example": "https://partner.bank.co.ke/hooks/pewang" },
          "secret": { "type": "string", "example": "super_shared_secret_for_hmac" }
        }
      }
    }
  },
  "paths": {
    "/api/mpesa/stk-push": {
      "post": {
        "tags": ["Payments"],
        "summary": "Initiate M-Pesa STK push",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/StkPushRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "STK request accepted",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StkPushResponse" }
              }
            }
          },
          "400": { "description": "Validation error or missing idempotency key" },
          "401": { "description": "Missing/invalid API key" },
          "409": { "description": "Idempotency key reused with different payload" }
        }
      }
    },
    "/api/mpesa/redeem-re": {
      "post": {
        "tags": ["Payments"],
        "summary": "Redeem third-party payer token and initiate STK",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RedeemReRequest" }
            }
          }
        },
        "responses": {
          "200": { "description": "Redeemed and STK initiated" },
          "400": { "description": "Invalid token or request" },
          "409": { "description": "Idempotency key reused with different payload" }
        }
      }
    },
    "/api/mpesa/stk-status/{checkoutRequestId}": {
      "get": {
        "tags": ["Payments"],
        "summary": "Fetch STK/settlement status",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/CheckoutRequestId" }],
        "responses": {
          "200": { "description": "Current status" },
          "403": { "description": "Forbidden for this transaction" },
          "404": { "description": "Not found" }
        }
      }
    },
    "/api/mpesa/transactions": {
      "get": {
        "tags": ["Reconciliation"],
        "summary": "List transaction rows for external reconciliation",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "from", "in": "query", "schema": { "type": "string", "format": "date-time" } },
          { "name": "to", "in": "query", "schema": { "type": "string", "format": "date-time" } },
          { "name": "status", "in": "query", "schema": { "type": "string", "example": "success" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 100 } }
        ],
        "responses": {
          "200": { "description": "Transaction list response" }
        }
      }
    },
    "/api/mpesa/reconciliation/summary": {
      "get": {
        "tags": ["Reconciliation"],
        "summary": "Get reconciliation summary totals",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          { "name": "from", "in": "query", "schema": { "type": "string", "format": "date-time" } },
          { "name": "to", "in": "query", "schema": { "type": "string", "format": "date-time" } }
        ],
        "responses": {
          "200": { "description": "Summary response with totals" }
        }
      }
    },
    "/api/mpesa/receipt/{checkoutRequestId}": {
      "get": {
        "tags": ["Receipts"],
        "summary": "Download PDF receipt by checkoutRequestId",
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/CheckoutRequestId" }],
        "responses": {
          "200": {
            "description": "PDF receipt binary",
            "content": {
              "application/pdf": {
                "schema": { "type": "string", "format": "binary" }
              }
            }
          },
          "404": { "description": "Transaction not found" }
        }
      }
    },
    "/api/user/webhook": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "Configure webhook URL and optional signature secret",
        "security": [{ "BearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/WebhookConfigRequest" }
            }
          }
        },
        "responses": {
          "200": { "description": "Webhook config saved" }
        }
      }
    }
  }
}
