{
  "openapi": "3.1.0",
  "info": {
    "title": "Padington Shop API",
    "version": "1.0.0",
    "description": "REST API for automating purchases of digital goods through franchise bots.\n\n**Auth**: Bearer token in `Authorization` header. Generate keys inside your bot (Profile → API keys).\n\n**Charging**: each successful order debits your bot balance atomically. Failed orders are auto-refunded.\n\n**Documentation**: https://api.padington.com/docs\n",
    "contact": {
      "name": "Padington Support",
      "url": "https://api.padington.com/docs"
    }
  },
  "servers": [
    {
      "url": "https://api.padington.com",
      "description": "Production"
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "pad_live_<random>",
        "description": "Bearer token starting with `pad_live_`. Generated inside the franchise bot."
      }
    },
    "schemas": {
      "Balance": {
        "type": "object",
        "properties": {
          "balance_rub": {
            "type": "number",
            "format": "double",
            "example": 5432.1
          }
        },
        "required": [
          "balance_rub"
        ]
      },
      "Catalog": {
        "type": "object",
        "properties": {
          "stars": {
            "type": "object",
            "properties": {
              "min_quantity": {
                "type": "integer",
                "example": 50
              },
              "max_quantity": {
                "type": "integer",
                "example": 1000000
              },
              "price_per_star_rub": {
                "type": "number",
                "example": 1.27
              }
            }
          },
          "premium": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "duration_months": {
                  "type": "integer",
                  "enum": [
                    3,
                    6,
                    12
                  ]
                },
                "price_rub": {
                  "type": "number"
                }
              }
            }
          },
          "chatgpt": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "example": "plus_1m"
                },
                "name": {
                  "type": "string",
                  "example": "Chat GPT Plus 1 month"
                },
                "price_rub": {
                  "type": "number"
                }
              }
            }
          },
          "claude": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "example": "pro"
                },
                "name": {
                  "type": "string"
                },
                "price_rub": {
                  "type": "number"
                }
              }
            }
          },
          "robux": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "pack": {
                  "type": "string",
                  "example": "r500"
                },
                "robux": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "price_rub": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "OrderRequest": {
        "type": "object",
        "required": [
          "category"
        ],
        "properties": {
          "category": {
            "type": "string",
            "enum": [
              "chatgpt",
              "claude",
              "robux",
              "stars",
              "premium"
            ]
          },
          "item": {
            "type": "string",
            "description": "For chatgpt/claude (plus_1m/plus_12m/pro_1m/pro/max5/max20)"
          },
          "quantity": {
            "type": "integer",
            "minimum": 50,
            "maximum": 1000000,
            "description": "Stars: 50–1M"
          },
          "recipient": {
            "type": "string",
            "description": "Telegram username for stars/premium (no @)"
          },
          "duration": {
            "type": "integer",
            "enum": [
              3,
              6,
              12
            ],
            "description": "Premium duration in months"
          },
          "pack": {
            "type": "string",
            "description": "Robux pack id, e.g. r80, r500"
          },
          "roblox_username": {
            "type": "string",
            "description": "Robux only"
          },
          "roblox_password": {
            "type": "string",
            "description": "Robux only"
          }
        }
      },
      "OrderResponse": {
        "type": "object",
        "properties": {
          "order_code": {
            "type": "string",
            "example": "API-1718183020-A3F5C9"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "needs_verification",
              "completed",
              "failed"
            ]
          },
          "amount_rub": {
            "type": "number"
          },
          "detail": {
            "type": "object"
          },
          "poll_url": {
            "type": "string"
          }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "order_code": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "item_key": {
            "type": "string"
          },
          "quantity": {
            "type": "integer"
          },
          "recipient": {
            "type": "string"
          },
          "amount_rub": {
            "type": "number"
          },
          "cost_rub": {
            "type": "number"
          },
          "profit_rub": {
            "type": "number"
          },
          "status": {
            "type": "string"
          },
          "external_id": {
            "type": "string"
          },
          "error": {
            "type": "string",
            "description": "On completed: JSON with codes. On failed: human-readable error."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "detail": {
            "type": "string"
          }
        }
      }
    }
  },
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "paths": {
    "/v1/balance": {
      "get": {
        "summary": "Get current balance",
        "description": "Returns the user's current balance in RUB.",
        "operationId": "getBalance",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                }
              }
            }
          },
          "401": {
            "description": "Invalid/revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "API disabled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/catalog": {
      "get": {
        "summary": "Get product catalog",
        "description": "Returns the full catalog with current RUB prices (floating with USD rate).",
        "operationId": "getCatalog",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Catalog"
                }
              }
            }
          }
        }
      }
    },
    "/v1/order": {
      "post": {
        "summary": "Create order",
        "description": "Creates a new order. Body shape depends on `category`. Balance is atomically debited at creation. If insufficient → 402.",
        "operationId": "createOrder",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Order created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid params"
          },
          "402": {
            "description": "Insufficient balance"
          }
        }
      }
    },
    "/v1/order/{order_code}": {
      "get": {
        "summary": "Get order status",
        "description": "Polling endpoint. CDK orders typically complete in 2-10 seconds.",
        "operationId": "getOrder",
        "parameters": [
          {
            "name": "order_code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Order details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/v1/orders": {
      "get": {
        "summary": "List orders",
        "description": "Paginated history in reverse chronological order.",
        "operationId": "listOrders",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "orders": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      }
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/rent/{category}": {
      "get": {
        "summary": "List NFTs available for rent",
        "description": "Returns a paginated list of rentable NFTs with RUB prices (franchise markup applied).",
        "operationId": "rentList",
        "parameters": [
          {
            "name": "category",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "gifts",
                "numbers",
                "usernames"
              ]
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "category": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/rent": {
      "post": {
        "summary": "Pay rent for NFT",
        "description": "Atomically debits balance and pays rent via marketapp. Refunds on failure.",
        "operationId": "rentPay",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "category",
                  "nft_address",
                  "duration_days"
                ],
                "properties": {
                  "category": {
                    "type": "string",
                    "enum": [
                      "gifts",
                      "numbers",
                      "usernames"
                    ]
                  },
                  "nft_address": {
                    "type": "string"
                  },
                  "duration_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365
                  },
                  "expected_price_rub": {
                    "type": "number",
                    "description": "5% drift tolerance; returns 409 if exceeded"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rent paid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient balance"
          },
          "404": {
            "description": "NFT not available"
          },
          "409": {
            "description": "Price drifted >5% or already rented"
          },
          "502": {
            "description": "Marketapp upstream error"
          }
        }
      }
    },
    "/v1/rentals": {
      "get": {
        "summary": "List your rentals",
        "description": "Returns your active and historical rentals.",
        "operationId": "rentMine",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  }
}