{
  "openapi": "3.0.3",
  "info": {
    "title": "LodumPayment API",
    "version": "1.0.0",
    "description": "One API for cards, bank transfer, and crypto across Africa. Accept payments, settle to bank or crypto wallet, run invoicing and subscriptions, and receive signed webhooks. All amounts are integers in the asset's smallest unit (kobo/cents for fiat, base units for crypto) — never floats."
  },
  "servers": [
    { "url": "http://localhost:8080", "description": "Local development" }
  ],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Auth", "description": "Signup, login, and session" },
    { "name": "Payments", "description": "Collect money" },
    { "name": "Payouts", "description": "Disburse money" },
    { "name": "Balances", "description": "Merchant balances" },
    { "name": "Invoices", "description": "Invoicing and collection" },
    { "name": "Billing", "description": "Prices, subscriptions, and usage (PAYG/PAYU)" },
    { "name": "Webhooks", "description": "Event subscriptions" },
    { "name": "Disputes", "description": "Complaints and chargebacks" }
  ],
  "paths": {
    "/v1/signup": {
      "post": {
        "tags": ["Auth"], "summary": "Create a merchant account", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SignupRequest" } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SignupResponse" } } } }, "409": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/v1/login": {
      "post": {
        "tags": ["Auth"], "summary": "Log in and receive a session token", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginRequest" } } } },
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginResponse" } } } }, "401": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/v1/me": {
      "get": { "tags": ["Auth"], "summary": "Current merchant profile", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Merchant" } } } } } },
      "patch": {
        "tags": ["Auth"], "summary": "Update profile (name, settlement preference)",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string" }, "crypto_settle": { "type": "string", "enum": ["auto_convert_ngn", "hold_stablecoin"] } } } } } },
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Merchant" } } } } }
      }
    },
    "/v1/me/password": {
      "post": {
        "tags": ["Auth"], "summary": "Change password",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["current_password", "new_password"], "properties": { "current_password": { "type": "string" }, "new_password": { "type": "string", "minLength": 8 } } } } } },
        "responses": { "200": { "description": "OK" }, "401": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/v1/api_keys": {
      "get": { "tags": ["Auth"], "summary": "List API keys", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/List" } } } } } },
      "post": {
        "tags": ["Auth"], "summary": "Issue a new API key",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "mode": { "type": "string", "enum": ["test", "live"] } } } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/APIKey" } } } } }
      }
    },
    "/v1/payment_intents": {
      "get": { "tags": ["Payments"], "summary": "List payment intents", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/List" } } } } } },
      "post": {
        "tags": ["Payments"], "summary": "Create a payment intent",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePaymentIntent" } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntent" } } } } }
      }
    },
    "/v1/payment_intents/{id}": {
      "get": { "tags": ["Payments"], "summary": "Retrieve a payment intent", "parameters": [{ "$ref": "#/components/parameters/Id" }], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntent" } } } }, "404": { "$ref": "#/components/responses/Error" } } }
    },
    "/v1/payment_intents/{id}/refund": {
      "post": {
        "tags": ["Payments"], "summary": "Refund a payment intent", "parameters": [{ "$ref": "#/components/parameters/Id" }, { "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "amount": { "$ref": "#/components/schemas/AmountInput" }, "reference": { "type": "string" }, "reason": { "type": "string" } } } } } },
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntent" } } } } }
      }
    },
    "/v1/payouts": {
      "get": { "tags": ["Payouts"], "summary": "List payouts", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/List" } } } } } },
      "post": {
        "tags": ["Payouts"], "summary": "Create a payout", "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayout" } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Payout" } } } }, "422": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/v1/payouts/{id}": {
      "get": { "tags": ["Payouts"], "summary": "Retrieve a payout", "parameters": [{ "$ref": "#/components/parameters/Id" }], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Payout" } } } } } }
    },
    "/v1/balances/{asset}": {
      "get": {
        "tags": ["Balances"], "summary": "Retrieve a balance",
        "parameters": [{ "name": "asset", "in": "path", "required": true, "schema": { "type": "string", "example": "NGN" } }, { "name": "network", "in": "query", "required": false, "schema": { "type": "string", "example": "TRON" } }],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Balance" } } } } }
      }
    },
    "/v1/invoices": {
      "get": { "tags": ["Invoices"], "summary": "List invoices", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/List" } } } } } },
      "post": {
        "tags": ["Invoices"], "summary": "Create an invoice",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateInvoice" } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Invoice" } } } } }
      }
    },
    "/v1/invoices/{id}": {
      "get": { "tags": ["Invoices"], "summary": "Retrieve an invoice", "parameters": [{ "$ref": "#/components/parameters/Id" }], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Invoice" } } } } } }
    },
    "/v1/invoices/{id}/pay": {
      "post": {
        "tags": ["Invoices"], "summary": "Collect an invoice", "parameters": [{ "$ref": "#/components/parameters/Id" }, { "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "rail": { "type": "string", "example": "rubies" } } } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntent" } } } } }
      }
    },
    "/v1/prices": {
      "get": { "tags": ["Billing"], "summary": "List prices", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/List" } } } } } },
      "post": {
        "tags": ["Billing"], "summary": "Create a price (recurring or usage)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "model": { "type": "string", "enum": ["recurring", "usage"] }, "currency": { "type": "string", "example": "NGN" }, "amount": { "type": "integer", "description": "per-interval charge (recurring)" }, "unit_amount": { "type": "integer", "description": "per-unit charge (usage)" }, "interval": { "type": "object", "properties": { "unit": { "type": "string", "enum": ["day", "week", "month", "year"] }, "count": { "type": "integer" } } } } } } } },
        "responses": { "201": { "description": "Created" } }
      }
    },
    "/v1/subscriptions": {
      "get": { "tags": ["Billing"], "summary": "List subscriptions", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/List" } } } } } },
      "post": {
        "tags": ["Billing"], "summary": "Start a subscription",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["price_id"], "properties": { "customer_id": { "type": "string" }, "price_id": { "type": "string" } } } } } },
        "responses": { "201": { "description": "Created" } }
      }
    },
    "/v1/subscriptions/{id}/cancel": {
      "post": { "tags": ["Billing"], "summary": "Cancel a subscription", "parameters": [{ "$ref": "#/components/parameters/Id" }], "responses": { "200": { "description": "OK" } } }
    },
    "/v1/subscriptions/{id}/usage": {
      "post": {
        "tags": ["Billing"], "summary": "Report metered usage (PAYG)", "parameters": [{ "$ref": "#/components/parameters/Id" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "quantity": { "type": "integer", "example": 42 } } } } } },
        "responses": { "201": { "description": "Recorded" } }
      }
    },
    "/v1/billing/run": {
      "post": { "tags": ["Billing"], "summary": "Generate due invoices now", "description": "Runs the billing engine for due subscriptions. In production a scheduler does this on an interval.", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/List" } } } } } }
    },
    "/v1/webhook_endpoints": {
      "get": { "tags": ["Webhooks"], "summary": "Get the current webhook endpoint (secret masked)", "responses": { "200": { "description": "OK" } } },
      "post": {
        "tags": ["Webhooks"], "summary": "Register or update a webhook endpoint",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["url", "secret"], "properties": { "url": { "type": "string", "example": "https://example.com/hooks" }, "secret": { "type": "string", "example": "whsec_..." } } } } } },
        "responses": { "201": { "description": "Created" } }
      },
      "delete": { "tags": ["Webhooks"], "summary": "Remove the webhook endpoint", "responses": { "200": { "description": "OK" } } }
    },
    "/v1/disputes": {
      "get": { "tags": ["Disputes"], "summary": "List disputes", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/List" } } } } } }
    },
    "/v1/disputes/{id}": {
      "get": { "tags": ["Disputes"], "summary": "Retrieve a dispute", "parameters": [{ "$ref": "#/components/parameters/Id" }], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Dispute" } } } } } }
    },
    "/v1/disputes/{id}/evidence": {
      "post": {
        "tags": ["Disputes"], "summary": "Add evidence to a dispute", "parameters": [{ "$ref": "#/components/parameters/Id" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "note": { "type": "string" } } } } } },
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Dispute" } } } } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Use a secret API key (sk_test_... / sk_live_...) or a session token from /v1/login as a Bearer token." }
    },
    "parameters": {
      "Id": { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
      "IdempotencyKey": { "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string" }, "description": "Safely retry a money-moving request: the same key returns the original result." }
    },
    "responses": {
      "Error": { "description": "Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "AmountInput": {
        "type": "object", "required": ["amount", "currency"],
        "properties": { "amount": { "type": "integer", "description": "Smallest unit (kobo/cents/base units)", "example": 1000000 }, "currency": { "type": "string", "example": "NGN" }, "network": { "type": "string", "description": "Required for crypto", "example": "TRON" } }
      },
      "Amount": {
        "type": "object",
        "properties": { "amount": { "type": "integer", "example": 985000 }, "currency": { "type": "string", "example": "NGN" }, "network": { "type": "string" }, "display": { "type": "string", "example": "NGN 9850.00" } }
      },
      "Error": { "type": "object", "properties": { "error": { "type": "object", "properties": { "type": { "type": "string", "example": "invalid_request" }, "message": { "type": "string" } } } } },
      "List": { "type": "object", "properties": { "object": { "type": "string", "example": "list" }, "data": { "type": "array", "items": { "type": "object" } } } },
      "SignupRequest": { "type": "object", "required": ["business_name", "email", "password"], "properties": { "business_name": { "type": "string" }, "email": { "type": "string" }, "password": { "type": "string", "minLength": 8 }, "country": { "type": "string", "example": "NG" } } },
      "SignupResponse": { "type": "object", "properties": { "merchant": { "$ref": "#/components/schemas/Merchant" }, "token": { "type": "string" }, "api_keys": { "type": "array", "items": { "$ref": "#/components/schemas/APIKey" } } } },
      "LoginRequest": { "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string" }, "password": { "type": "string" } } },
      "LoginResponse": { "type": "object", "properties": { "merchant": { "$ref": "#/components/schemas/Merchant" }, "token": { "type": "string" } } },
      "Merchant": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "type": "string", "example": "merchant" }, "name": { "type": "string" }, "email": { "type": "string" }, "jurisdiction": { "type": "string" }, "kyb": { "type": "string", "enum": ["pending", "verified", "rejected"] }, "fee_bps": { "type": "integer" }, "settle_fiat": { "type": "string" }, "crypto_settle": { "type": "string" } } },
      "APIKey": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "type": "string", "example": "api_key" }, "mode": { "type": "string", "enum": ["test", "live"] }, "secret": { "type": "string", "example": "sk_test_..." } } },
      "CreatePaymentIntent": {
        "type": "object", "required": ["amount", "method", "rail"],
        "properties": { "amount": { "$ref": "#/components/schemas/AmountInput" }, "method": { "type": "string", "enum": ["bank_transfer", "virtual_account", "card", "crypto"] }, "rail": { "type": "string", "example": "rubies" }, "settle_as": { "$ref": "#/components/schemas/AmountInput" }, "rate_key": { "type": "string" }, "customer_id": { "type": "string" }, "metadata": { "type": "object", "additionalProperties": { "type": "string" } } }
      },
      "PaymentIntent": {
        "type": "object",
        "properties": { "id": { "type": "string", "example": "pi_000001" }, "object": { "type": "string", "example": "payment_intent" }, "merchant": { "type": "string" }, "amount": { "$ref": "#/components/schemas/Amount" }, "settle_as": { "type": "string" }, "method": { "type": "string" }, "rail": { "type": "string" }, "status": { "type": "string", "enum": ["created", "requires_action", "processing", "succeeded", "failed", "refunded", "partially_refunded"] }, "provider_ref": { "type": "string" }, "next_action": { "$ref": "#/components/schemas/NextAction" }, "created_at": { "type": "string" } }
      },
      "NextAction": { "type": "object", "properties": { "type": { "type": "string", "enum": ["redirect", "bank_transfer", "crypto_deposit", "none"] }, "url": { "type": "string" }, "account_no": { "type": "string" }, "bank_name": { "type": "string" }, "crypto_chain": { "type": "string" }, "crypto_addr": { "type": "string" } } },
      "CreatePayout": {
        "type": "object", "required": ["amount", "rail", "destination"],
        "properties": { "amount": { "$ref": "#/components/schemas/AmountInput" }, "rail": { "type": "string" }, "reference": { "type": "string" }, "destination": { "type": "object", "properties": { "kind": { "type": "string", "enum": ["bank", "crypto"] }, "bank_code": { "type": "string" }, "account_no": { "type": "string" }, "account_name": { "type": "string" }, "crypto_chain": { "type": "string" }, "crypto_addr": { "type": "string" } } } }
      },
      "Payout": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "type": "string", "example": "payout" }, "merchant": { "type": "string" }, "amount": { "$ref": "#/components/schemas/Amount" }, "rail": { "type": "string" }, "status": { "type": "string", "enum": ["scheduled", "processing", "paid", "failed", "returned"] }, "provider_ref": { "type": "string" }, "created_at": { "type": "string" } } },
      "Balance": { "type": "object", "properties": { "object": { "type": "string", "example": "balance" }, "asset": { "type": "string" }, "available": { "$ref": "#/components/schemas/Amount" }, "pending": { "$ref": "#/components/schemas/Amount" }, "reserved": { "$ref": "#/components/schemas/Amount" } } },
      "CreateInvoice": {
        "type": "object", "required": ["currency", "lines"],
        "properties": { "customer_id": { "type": "string" }, "currency": { "type": "string", "example": "NGN" }, "network": { "type": "string" }, "due_at": { "type": "string", "format": "date-time" }, "lines": { "type": "array", "items": { "type": "object", "properties": { "description": { "type": "string" }, "quantity": { "type": "integer" }, "unit_amount": { "type": "integer" } } } } }
      },
      "Invoice": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "type": "string", "example": "invoice" }, "merchant": { "type": "string" }, "customer": { "type": "string" }, "total": { "$ref": "#/components/schemas/Amount" }, "status": { "type": "string", "enum": ["draft", "open", "paid", "void", "uncollectible"] }, "due_at": { "type": "string" } } },
      "Dispute": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "type": "string", "example": "dispute" }, "merchant": { "type": "string" }, "intent": { "type": "string" }, "reason": { "type": "string" }, "amount": { "$ref": "#/components/schemas/Amount" }, "status": { "type": "string", "enum": ["open", "under_review", "resolved_merchant", "resolved_customer"] }, "evidence": { "type": "array", "items": { "type": "object" } }, "created_at": { "type": "string" } } }
    }
  }
}
