{
  "openapi": "3.1.0",
  "info": {
    "title": "psfp5 Payments API",
    "version": "0.2.0",
    "description": "H2H Cashier API, Hosted Payment Page API, non-custodial Payouts and Balance for merchant integrations."
  },
  "servers": [
    { "url": "https://{cashierDomain}", "variables": { "cashierDomain": { "default": "pay.example.com" } } }
  ],
  "components": {
    "securitySchemes": {
      "ApiKey": { "type": "apiKey", "in": "header", "name": "X-Api-Key" },
      "Timestamp": { "type": "apiKey", "in": "header", "name": "X-Timestamp" },
      "Nonce": { "type": "apiKey", "in": "header", "name": "X-Nonce" },
      "Signature": { "type": "apiKey", "in": "header", "name": "X-Signature" }
    },
    "schemas": {
      "InitRequest": {
        "type": "object",
        "properties": {
          "player_id": { "type": "string" },
          "external_order_id": { "type": "string" },
          "callback_url": { "type": "string", "format": "uri", "description": "Per-session override of the merchant's configured webhook target." },
          "amount": { "type": "string", "description": "Optional prefill in whole token units, e.g. \"125.50\". Skips the enter_amount step." },
          "network": { "type": "string", "description": "Optional prefill, e.g. \"eth\". Supplying it skips the network_select step." },
          "token": { "type": "string", "description": "Optional prefill, e.g. \"USDT\". Requires network; supplying both starts the session at connect_wallet." }
        }
      },
      "StepRequest": {
        "type": "object",
        "required": ["session", "action"],
        "properties": {
          "session": { "type": "string" },
          "action": { "type": "string" },
          "value": { "type": "string" }
        }
      },
      "StatusRequest": {
        "type": "object",
        "required": ["session"],
        "properties": {
          "session": { "type": "string" }
        }
      },
      "HPPCreateRequest": {
        "type": "object",
        "properties": {
          "player_id": { "type": "string" },
          "external_order_id": { "type": "string" },
          "return_url": { "type": "string", "format": "uri" },
          "amount": { "type": "string", "description": "Optional prefill: fixes the deposit amount so the hosted page skips the amount step." },
          "network": { "type": "string", "description": "Optional prefill, e.g. \"eth\"." },
          "token": { "type": "string", "description": "Optional prefill, e.g. \"USDT\" or \"USDC\"." },
          "callback_url": { "type": "string", "format": "uri", "description": "Per-session override of the merchant's configured webhook target." }
        }
      },
      "HPPCreateResponse": {
        "type": "object",
        "required": ["session", "token", "url"],
        "properties": {
          "session": { "type": "string" },
          "token": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "expires_at": { "type": "string", "format": "date-time" }
        }
      },
      "HPPStepRequest": {
        "type": "object",
        "required": ["token", "action"],
        "properties": {
          "token": { "type": "string" },
          "action": { "type": "string" },
          "value": { "type": "string" }
        }
      },
      "HPPStatusRequest": {
        "type": "object",
        "required": ["token"],
        "properties": {
          "token": { "type": "string" }
        }
      },
      "StepResponse": {
        "type": "object",
        "required": ["step", "session"],
        "properties": {
          "step": {
            "type": "string",
            "enum": ["network_select", "token_select", "connect_wallet", "enter_amount", "awaiting_approval", "awaiting_payment", "processing", "completed", "failed"]
          },
          "session": { "type": "string" },
          "components": { "type": "array", "items": { "$ref": "#/components/schemas/Component" } },
          "wallet_action": { "$ref": "#/components/schemas/WalletAction" },
          "submit_action": { "type": "string" }
        }
      },
      "Component": {
        "type": "object",
        "required": ["type"],
        "properties": {
          "type": { "type": "string", "enum": ["heading", "text", "button_group", "input", "status", "spinner", "wallet_connect"] },
          "text": { "type": "string" },
          "options": { "type": "array", "items": { "$ref": "#/components/schemas/Option" } },
          "name": { "type": "string" },
          "label": { "type": "string" },
          "placeholder": { "type": "string" },
          "action": { "type": "string" },
          "status": { "type": "string", "enum": ["info", "success", "error"] }
        }
      },
      "Option": {
        "type": "object",
        "required": ["label", "action", "value"],
        "properties": {
          "label": { "type": "string" },
          "action": { "type": "string" },
          "value": { "type": "string" }
        }
      },
      "WalletAction": {
        "type": "object",
        "required": ["method", "params"],
        "properties": {
          "method": { "type": "string", "example": "eth_sendTransaction" },
          "params": { "type": "array", "items": true },
          "chain_id": { "type": "string", "example": "0xaa36a7" }
        }
      },
      "PayoutCreateRequest": {
        "type": "object",
        "required": ["network", "token", "amount", "recipient"],
        "properties": {
          "external_id": { "type": "string", "description": "Merchant idempotency key: at most one payout per (merchant, external_id); repeats return the existing payout." },
          "network": { "type": "string", "example": "eth" },
          "token": { "type": "string", "example": "USDT" },
          "amount": { "type": "string", "example": "125.50" },
          "recipient": { "type": "string", "description": "Destination EVM address (0x + 40 hex chars)." }
        }
      },
      "PayoutAuthorizeRequest": {
        "type": "object",
        "required": ["payout", "signature"],
        "properties": {
          "payout": { "type": "string" },
          "signature": { "type": "string", "description": "65-byte EIP-712 signature (eth_signTypedData_v4) over the typed_data returned by create." }
        }
      },
      "PayoutStatusRequest": {
        "type": "object",
        "required": ["payout"],
        "properties": {
          "payout": { "type": "string" }
        }
      },
      "PayoutResponse": {
        "type": "object",
        "required": ["payout", "status", "network", "token", "amount", "recipient"],
        "properties": {
          "payout": { "type": "string" },
          "status": { "type": "string", "enum": ["pending", "authorized", "submitted", "completed", "failed"] },
          "network": { "type": "string" },
          "token": { "type": "string" },
          "amount": { "type": "string" },
          "recipient": { "type": "string" },
          "tx_hash": { "type": "string", "description": "Relay transaction hash; present once the payout is submitted." },
          "typed_data": { "type": "object", "description": "EIP-712 payload to sign with eth_signTypedData_v4. Present only on create." }
        }
      },
      "BalanceResponse": {
        "type": "object",
        "required": ["wallet", "balances"],
        "properties": {
          "wallet": { "type": "string", "description": "The merchant's settlement wallet address." },
          "balances": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["network", "token"],
              "properties": {
                "network": { "type": "string" },
                "token": { "type": "string" },
                "address": { "type": "string", "description": "Token contract address." },
                "decimals": { "type": "integer" },
                "amount": { "type": "string", "description": "Human-decimal balance; absent when the read failed." },
                "raw": { "type": "string", "description": "Integer base-unit balance." },
                "error": { "type": "string", "description": "Present instead of amount when this token's read failed." }
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "enum": ["not_found", "validation", "invalid_action", "unsupported", "conflict", "unauthorized", "forbidden", "expired", "internal"]
              },
              "message": { "type": "string" }
            }
          }
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error response",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" }
          }
        }
      }
    }
  },
  "paths": {
    "/healthz": {
      "get": {
        "summary": "Health check",
        "responses": {
          "200": { "description": "OK" }
        }
      }
    },
    "/v1/cashier/init": {
      "post": {
        "summary": "Create H2H cashier session",
        "security": [{ "ApiKey": [], "Timestamp": [], "Nonce": [], "Signature": [] }],
        "requestBody": {
          "content": {
            "application/json": { "schema": { "$ref": "#/components/schemas/InitRequest" } }
          }
        },
        "responses": {
          "200": { "description": "First step", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StepResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/cashier/step": {
      "post": {
        "summary": "Advance H2H cashier session",
        "security": [{ "ApiKey": [], "Timestamp": [], "Nonce": [], "Signature": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StepRequest" } } } },
        "responses": {
          "200": { "description": "Next step", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StepResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/cashier/status": {
      "post": {
        "summary": "Read current H2H cashier step",
        "security": [{ "ApiKey": [], "Timestamp": [], "Nonce": [], "Signature": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusRequest" } } } },
        "responses": {
          "200": { "description": "Current step", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StepResponse" } } } },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/payouts": {
      "post": {
        "summary": "Create payout order (returns EIP-712 typed_data to sign)",
        "description": "Builds a non-custodial payout order pulled from the merchant's settlement wallet. Requires a prior ERC-20 approve(router, cap) from that wallet. Idempotent on external_id. The order is signable for 30 minutes.",
        "security": [{ "ApiKey": [], "Timestamp": [], "Nonce": [], "Signature": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreateRequest" } } } },
        "responses": {
          "200": { "description": "Payout with typed_data to sign", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/payouts/authorize": {
      "post": {
        "summary": "Submit merchant EIP-712 signature; payout is then relayed on-chain",
        "security": [{ "ApiKey": [], "Timestamp": [], "Nonce": [], "Signature": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutAuthorizeRequest" } } } },
        "responses": {
          "200": { "description": "Authorized payout", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "410": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/payouts/status": {
      "post": {
        "summary": "Read payout status (safe to poll)",
        "security": [{ "ApiKey": [], "Timestamp": [], "Nonce": [], "Signature": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutStatusRequest" } } } },
        "responses": {
          "200": { "description": "Current payout state", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutResponse" } } } },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/balance": {
      "get": {
        "summary": "On-chain settlement wallet balances",
        "description": "Signed like any other request; the canonical string uses GET, the path, and the empty-body hash.",
        "security": [{ "ApiKey": [], "Timestamp": [], "Nonce": [], "Signature": [] }],
        "responses": {
          "200": { "description": "Live token balances", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BalanceResponse" } } } },
          "401": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/hpp/sessions": {
      "post": {
        "summary": "Create hosted payment page session",
        "security": [{ "ApiKey": [], "Timestamp": [], "Nonce": [], "Signature": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HPPCreateRequest" } } } },
        "responses": {
          "200": { "description": "Hosted link", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HPPCreateResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/p/{token}": {
      "get": {
        "summary": "Hosted payment page",
        "parameters": [{ "name": "token", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "HTML hosted page" }
        }
      }
    },
    "/v1/hpp/status": {
      "post": {
        "summary": "Read hosted session status",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HPPStatusRequest" } } } },
        "responses": {
          "200": { "description": "Current step", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StepResponse" } } } },
          "410": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/hpp/step": {
      "post": {
        "summary": "Advance hosted session",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HPPStepRequest" } } } },
        "responses": {
          "200": { "description": "Next step", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StepResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "410": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/hpp/sdk.js": {
      "get": {
        "summary": "Iframe modal SDK",
        "responses": {
          "200": { "description": "JavaScript SDK" }
        }
      }
    }
  }
}
