{
  "openapi": "3.0.3",
  "info": {
    "title": "CashPay Merchant API",
    "version": "1.1.0",
    "description": "HMAC-authenticated Merchant API for Lightning payments. Amount may be a package_id or amount_usd (not both). Effective max is live inbound capacity − 50,000 sats; minimum is $1 system-wide."
  },
  "servers": [
    {
      "url": "https://clerpay.com",
      "description": "Merchant API root. Hosted checkout pay_url uses this host."
    }
  ],
  "tags": [
    { "name": "System" },
    { "name": "Packages" },
    { "name": "Limits" },
    { "name": "Payments" }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "tags": ["System"],
        "summary": "Health check",
        "description": "Public connectivity probe. No authentication.",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "Service is up",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean", "example": true },
                    "service": { "type": "string", "example": "cashpay-merchant-api" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/packages": {
      "get": {
        "tags": ["Packages"],
        "summary": "List packages",
        "operationId": "listPackages",
        "security": [{ "ApiKeyAuth": [], "HmacAuth": [] }],
        "responses": {
          "200": {
            "description": "Enabled packages for the merchant",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Package" }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/limits": {
      "get": {
        "tags": ["Limits"],
        "summary": "Amount limits",
        "description": "Returns amount_mode (always both) and site-wide min/max USD (live inbound cap − 50k sats). Min is $1.",
        "operationId": "getLimits",
        "security": [{ "ApiKeyAuth": [], "HmacAuth": [] }],
        "responses": {
          "200": {
            "description": "Current limits",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Limits" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/payments": {
      "post": {
        "tags": ["Payments"],
        "summary": "Create payment",
        "description": "Send either package_id or amount_usd (never both). amount_mode is always both. Amounts are capped by GET /v1/limits. Same merchant_order_no with the same amount and package is idempotent (200); different amount/package returns 409.",
        "operationId": "createPayment",
        "security": [{ "ApiKeyAuth": [], "HmacAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreatePaymentRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Payment created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Payment" }
              }
            }
          },
          "200": {
            "description": "Idempotent replay of an existing payment (same amount and package)",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Payment" }
              }
            }
          },
          "409": {
            "description": "merchant_order_no already used with a different amount or package",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/Unprocessable" }
        }
      },
      "get": {
        "tags": ["Payments"],
        "summary": "Get payment by merchant order number",
        "operationId": "getPaymentByOrder",
        "security": [{ "ApiKeyAuth": [], "HmacAuth": [] }],
        "parameters": [
          {
            "name": "merchant_order_no",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "maxLength": 64 }
          }
        ],
        "responses": {
          "200": {
            "description": "Payment found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Payment" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/Unprocessable" }
        }
      }
    },
    "/v1/payments/{id}": {
      "get": {
        "tags": ["Payments"],
        "summary": "Get payment by id",
        "operationId": "getPaymentById",
        "security": [{ "ApiKeyAuth": [], "HmacAuth": [] }],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Payment found",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Payment" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Api-Key"
      },
      "HmacAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Signature",
        "description": "Lowercase hex HMAC-SHA256 of the canonical string. Also send X-Timestamp and X-Nonce. See Merchant API docs."
      }
    },
    "schemas": {
      "Package": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string" },
          "amount_usd": { "type": "string", "example": "100.00" },
          "sort_order": { "type": "integer" }
        }
      },
      "Limits": {
        "type": "object",
        "properties": {
          "amount_mode": {
            "type": "string",
            "enum": ["both"],
            "description": "Always both: send either package_id or amount_usd (not both in one request)"
          },
          "min_amount_usd": { "type": "string", "example": "1.00" },
          "max_amount_usd": {
            "type": "string",
            "example": "3180.15",
            "description": "Site-wide max = live max inbound channel − 50,000 sats, in USD"
          },
          "config_max_amount_usd": {
            "type": "string",
            "example": "3180.15",
            "description": "Same as max_amount_usd (merchant per-profile caps removed)"
          },
          "live_max_amount_usd": { "type": "string", "nullable": true, "example": "3180.15" },
          "reserve_sats": { "type": "integer", "example": 50000 },
          "inbound_sats": { "type": "integer", "nullable": true }
        }
      },
      "CreatePaymentRequest": {
        "type": "object",
        "required": ["merchant_order_no"],
        "properties": {
          "package_id": {
            "type": "string",
            "format": "uuid",
            "description": "Required in package mode; allowed in both mode. Do not send together with amount_usd."
          },
          "amount_usd": {
            "type": "number",
            "description": "Required in custom mode; allowed in both mode. Must be within GET /v1/limits. Do not send together with package_id."
          },
          "merchant_order_no": { "type": "string", "maxLength": 64 },
          "notify_url": { "type": "string", "format": "uri" },
          "metadata": { "type": "object", "additionalProperties": true }
        }
      },
      "Payment": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "merchant_order_no": { "type": "string" },
          "package_id": { "type": "string", "format": "uuid", "nullable": true },
          "package_name": { "type": "string", "nullable": true },
          "amount_usd": { "type": "string" },
          "status": { "type": "string", "enum": ["pending", "paid", "expired"] },
          "bolt11": { "type": "string", "nullable": true },
          "pay_url": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Hosted checkout URL (https://clerpay.com/pay/invoice/{invoice_id}).",
            "example": "https://clerpay.com/pay/invoice/9236f138-07b5-4d1c-9fe6-bea17a29bc06"
          },
          "expires_at": { "type": "string", "nullable": true },
          "paid_at": { "type": "string", "nullable": true },
          "created_at": { "type": "string" },
          "metadata": { "nullable": true }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "message": { "type": "string" }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing/invalid auth headers or signature",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "Forbidden": {
        "description": "API disabled or IP not allowlisted",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "Unprocessable": {
        "description": "Validation error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    }
  }
}
