{
  "openapi": "3.0.3",
  "info": {
    "title": "CowemaPay API",
    "description": "API de paiement Mobile Money (MTN MoMo, Airtel Money).\n\n## Idempotence\n\nLes requetes POST acceptent un en-tete optionnel `Idempotency-Key` (chaine de caracteres). Si la meme cle est reutilisee avec le meme corps de requete, la reponse originale est retournee pendant 30 jours. Si la cle est reutilisee avec un corps de requete different, une erreur 409 `idempotency_conflict` est retournee.\n\n## Limitation de debit\n\nPar defaut, l'API autorise 100 requetes par minute par cle API. Les en-tetes de reponse incluent :\n- `X-RateLimit-Limit` : nombre maximum de requetes autorisees\n- `X-RateLimit-Remaining` : nombre de requetes restantes\n- `Retry-After` : nombre de secondes avant la prochaine fenetre (present uniquement en cas de depassement)\n\n## Restriction par IP\n\nChaque application peut configurer une liste blanche d'adresses IP autorisees. Si l'adresse IP de la requete n'est pas dans la liste, une erreur 403 `ip_not_whitelisted` est retournee.\n\n## Verification KYC\n\nLe mode production (`sk_live_*`) necessite une verification KYC validee. Si la verification n'est pas effectuee, une erreur 403 `kyc_verification_required` est retournee.",
    "version": "1.0.0",
    "contact": {
      "name": "CowemaPay",
      "url": "https://cowemapay.com"
    }
  },
  "servers": [
    {
      "url": "https://pay.cowema.org/api/v1",
      "description": "Production"
    },
    {
      "url": "http://127.0.0.1:8000/api/v1",
      "description": "Local"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "paths": {
    "/payments": {
      "post": {
        "summary": "Creer un paiement",
        "operationId": "createPayment",
        "tags": ["Paiements"],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Cle d'idempotence pour eviter les doublons. Meme cle = meme reponse pendant 30 jours.",
            "schema": { "type": "string", "example": "ord-123-payment-001" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePaymentRequest"
              },
              "example": {
                "amount": 5000,
                "country": "CG",
                "phone_number": "068510000",
                "provider": "mtn_momo",
                "metadata": {
                  "order_id": "ORD-123"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Paiement cree",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Transaction"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "550e8400-e29b-41d4-a716-446655440000",
                    "type": "collection",
                    "provider": "mtn_momo",
                    "amount": 5000,
                    "currency": "XAF",
                    "phone_number": "068510000",
                    "status": "pending",
                    "failure_reason": null,
                    "environment": "test",
                    "metadata": { "order_id": "ORD-123" },
                    "created_at": "2026-03-29T10:00:00Z",
                    "updated_at": "2026-03-29T10:00:00Z"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "409": { "$ref": "#/components/responses/IdempotencyConflict" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      },
      "get": {
        "summary": "Lister les paiements",
        "operationId": "listPayments",
        "tags": ["Paiements"],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": { "type": "string", "enum": ["pending", "processing", "successful", "failed", "cancelled"] }
          },
          {
            "name": "provider",
            "in": "query",
            "schema": { "type": "string", "enum": ["mtn_momo", "airtel_money"] }
          },
          {
            "name": "environment",
            "in": "query",
            "schema": { "type": "string", "enum": ["live", "test"] }
          },
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginee",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Transaction" }
                    },
                    "links": { "$ref": "#/components/schemas/PaginationLinks" },
                    "meta": { "$ref": "#/components/schemas/PaginationMeta" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/payments/{id}": {
      "get": {
        "summary": "Voir un paiement",
        "operationId": "getPayment",
        "tags": ["Paiements"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" },
            "example": "550e8400-e29b-41d4-a716-446655440000"
          }
        ],
        "responses": {
          "200": {
            "description": "Details du paiement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Transaction" }
                  }
                },
                "example": {
                  "data": {
                    "id": "550e8400-e29b-41d4-a716-446655440000",
                    "type": "collection",
                    "provider": "mtn_momo",
                    "amount": 5000,
                    "currency": "XAF",
                    "phone_number": "068510000",
                    "status": "successful",
                    "failure_reason": null,
                    "environment": "test",
                    "metadata": { "order_id": "ORD-123" },
                    "created_at": "2026-03-29T10:00:00Z",
                    "updated_at": "2026-03-29T10:05:00Z"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/refunds": {
      "post": {
        "summary": "Creer un remboursement",
        "operationId": "createRefund",
        "tags": ["Remboursements"],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Cle d'idempotence pour eviter les doublons. Meme cle = meme reponse pendant 30 jours.",
            "schema": { "type": "string", "example": "refund-ord-123-001" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRefundRequest"
              },
              "example": {
                "payment_id": "550e8400-e29b-41d4-a716-446655440000",
                "amount": 5000
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Remboursement cree",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Transaction" }
                  }
                },
                "example": {
                  "data": {
                    "id": "660e8400-e29b-41d4-a716-446655440001",
                    "type": "refund",
                    "provider": "mtn_momo",
                    "amount": 5000,
                    "currency": "XAF",
                    "phone_number": "068510000",
                    "status": "pending",
                    "failure_reason": null,
                    "environment": "test",
                    "metadata": null,
                    "created_at": "2026-03-29T11:00:00Z",
                    "updated_at": "2026-03-29T11:00:00Z"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "409": { "$ref": "#/components/responses/IdempotencyConflict" },
          "422": { "$ref": "#/components/responses/RefundValidationError" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/refunds/{id}": {
      "get": {
        "summary": "Voir un remboursement",
        "operationId": "getRefund",
        "tags": ["Remboursements"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" },
            "example": "660e8400-e29b-41d4-a716-446655440001"
          }
        ],
        "responses": {
          "200": {
            "description": "Details du remboursement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Transaction" }
                  }
                },
                "example": {
                  "data": {
                    "id": "660e8400-e29b-41d4-a716-446655440001",
                    "type": "refund",
                    "provider": "mtn_momo",
                    "amount": 5000,
                    "currency": "XAF",
                    "phone_number": "068510000",
                    "status": "successful",
                    "failure_reason": null,
                    "environment": "test",
                    "metadata": null,
                    "created_at": "2026-03-29T11:00:00Z",
                    "updated_at": "2026-03-29T11:05:00Z"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/checkout/sessions": {
      "post": {
        "summary": "Creer une session checkout",
        "operationId": "createCheckoutSession",
        "tags": ["Checkout"],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Cle d'idempotence pour eviter les doublons. Meme cle = meme reponse pendant 30 jours.",
            "schema": { "type": "string", "example": "checkout-ord-456-001" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCheckoutSessionRequest"
              },
              "example": {
                "amount": 10000,
                "currency": "XAF",
                "description": "Abonnement Premium",
                "success_url": "https://votresite.com/success",
                "cancel_url": "https://votresite.com/cancel"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session creee",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/CheckoutSession" }
                  }
                },
                "example": {
                  "data": {
                    "id": "770e8400-e29b-41d4-a716-446655440002",
                    "amount": 10000,
                    "currency": "XAF",
                    "description": "Abonnement Premium",
                    "url": "https://pay.cowema.org/checkout/770e8400-e29b-41d4-a716-446655440002",
                    "status": "open",
                    "expires_at": "2026-03-29T11:30:00Z",
                    "metadata": null,
                    "created_at": "2026-03-29T11:00:00Z"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "409": { "$ref": "#/components/responses/IdempotencyConflict" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/checkout/sessions/{id}": {
      "get": {
        "summary": "Voir une session checkout",
        "operationId": "getCheckoutSession",
        "tags": ["Checkout"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" },
            "example": "770e8400-e29b-41d4-a716-446655440002"
          }
        ],
        "responses": {
          "200": {
            "description": "Details de la session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/CheckoutSession" }
                  }
                },
                "example": {
                  "data": {
                    "id": "770e8400-e29b-41d4-a716-446655440002",
                    "amount": 10000,
                    "currency": "XAF",
                    "description": "Abonnement Premium",
                    "url": "https://pay.cowema.org/checkout/770e8400-e29b-41d4-a716-446655440002",
                    "status": "completed",
                    "expires_at": "2026-03-29T11:30:00Z",
                    "metadata": null,
                    "created_at": "2026-03-29T11:00:00Z"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/balance": {
      "get": {
        "summary": "Consulter le solde fournisseur",
        "operationId": "getBalance",
        "tags": ["Solde"],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "enum": ["mtn_momo", "airtel_money"] },
            "example": "mtn_momo"
          }
        ],
        "responses": {
          "200": {
            "description": "Solde du compte fournisseur",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "available": { "type": "integer", "example": 1000000 },
                        "currency": { "type": "string", "example": "XAF" }
                      }
                    }
                  }
                },
                "example": {
                  "data": {
                    "available": 1000000,
                    "currency": "XAF"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/payouts": {
      "post": {
        "summary": "Creer un decaissement",
        "operationId": "createPayout",
        "tags": ["Decaissements"],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Cle d'idempotence pour eviter les doublons. Meme cle = meme reponse pendant 30 jours.",
            "schema": { "type": "string", "example": "payout-vendor-789-001" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePayoutRequest"
              },
              "example": {
                "amount": 25000,
                "currency": "XAF",
                "country": "CG",
                "provider": "mtn_momo",
                "phone_number": "068510000"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Decaissement cree",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Payout" }
                  }
                },
                "example": {
                  "data": {
                    "id": "880e8400-e29b-41d4-a716-446655440003",
                    "amount": 25000,
                    "fee_amount": 500,
                    "total_deducted": 25500,
                    "currency": "XAF",
                    "provider": "mtn_momo",
                    "country": "CG",
                    "phone_number": "068510000",
                    "status": "pending",
                    "reference": "PAY-2026-0001",
                    "created_at": "2026-03-29T12:00:00Z"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "409": { "$ref": "#/components/responses/IdempotencyConflict" },
          "422": {
            "description": "Erreur de validation ou solde insuffisant",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/ValidationErrorResponse" },
                    { "$ref": "#/components/schemas/ErrorResponse" }
                  ]
                },
                "examples": {
                  "validation_error": {
                    "summary": "Erreur de validation",
                    "value": {
                      "message": "The amount field is required.",
                      "errors": {
                        "amount": ["The amount field is required."],
                        "country": ["The country field is required."]
                      }
                    }
                  },
                  "insufficient_balance": {
                    "summary": "Solde insuffisant",
                    "value": {
                      "error": "Solde insuffisant.",
                      "code": "insufficient_balance"
                    }
                  }
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      },
      "get": {
        "summary": "Lister les decaissements",
        "operationId": "listPayouts",
        "tags": ["Decaissements"],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": { "type": "string", "enum": ["pending", "approved", "processing", "completed", "failed", "rejected"] }
          },
          {
            "name": "per_page",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
          },
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "default": 1 }
          }
        ],
        "responses": {
          "200": {
            "description": "Liste paginee des decaissements",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Payout" }
                    },
                    "links": { "$ref": "#/components/schemas/PaginationLinks" },
                    "meta": { "$ref": "#/components/schemas/PaginationMeta" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/payouts/{id}": {
      "get": {
        "summary": "Voir un decaissement",
        "operationId": "getPayout",
        "tags": ["Decaissements"],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" },
            "example": "880e8400-e29b-41d4-a716-446655440003"
          }
        ],
        "responses": {
          "200": {
            "description": "Details du decaissement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": { "$ref": "#/components/schemas/Payout" }
                  }
                },
                "example": {
                  "data": {
                    "id": "880e8400-e29b-41d4-a716-446655440003",
                    "amount": 25000,
                    "fee_amount": 500,
                    "total_deducted": 25500,
                    "currency": "XAF",
                    "provider": "mtn_momo",
                    "country": "CG",
                    "phone_number": "068510000",
                    "status": "completed",
                    "reference": "PAY-2026-0001",
                    "created_at": "2026-03-29T12:00:00Z"
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/merchant/balance": {
      "get": {
        "summary": "Consulter le solde marchand",
        "operationId": "getMerchantBalance",
        "tags": ["Solde marchand"],
        "responses": {
          "200": {
            "description": "Soldes par devise de l'organisation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/MerchantBalance" }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "currency": "XAF",
                      "available_balance": 15885960,
                      "pending_balance": 0
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Cle secrete (sk_test_... ou sk_live_...)"
      }
    },
    "schemas": {
      "Transaction": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid", "example": "550e8400-e29b-41d4-a716-446655440000" },
          "type": { "type": "string", "enum": ["collection", "disbursement", "refund"], "example": "collection" },
          "provider": { "type": "string", "enum": ["mtn_momo", "airtel_money"], "example": "mtn_momo" },
          "amount": { "type": "integer", "example": 5000 },
          "currency": { "type": "string", "example": "XAF" },
          "phone_number": { "type": "string", "example": "068510000" },
          "status": { "type": "string", "enum": ["pending", "processing", "successful", "failed", "cancelled"], "example": "successful" },
          "failure_reason": { "type": "string", "nullable": true, "example": null },
          "environment": { "type": "string", "enum": ["live", "test"], "example": "test" },
          "metadata": { "type": "object", "nullable": true, "example": { "order_id": "ORD-123" } },
          "created_at": { "type": "string", "format": "date-time", "example": "2026-03-29T10:00:00Z" },
          "updated_at": { "type": "string", "format": "date-time", "example": "2026-03-29T10:05:00Z" }
        }
      },
      "Payout": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid", "example": "880e8400-e29b-41d4-a716-446655440003" },
          "amount": { "type": "integer", "description": "Montant du decaissement", "example": 25000 },
          "fee_amount": { "type": "integer", "description": "Frais appliques", "example": 500 },
          "total_deducted": { "type": "integer", "description": "Total debite du solde (montant + frais)", "example": 25500 },
          "currency": { "type": "string", "description": "Code devise ISO 4217", "example": "XAF" },
          "provider": { "type": "string", "enum": ["mtn_momo", "airtel_money"], "example": "mtn_momo" },
          "country": { "type": "string", "description": "Code pays ISO 3166-1 alpha-2", "example": "CG" },
          "phone_number": { "type": "string", "example": "068510000" },
          "status": { "type": "string", "enum": ["pending", "approved", "processing", "completed", "failed", "rejected"], "example": "pending" },
          "reference": { "type": "string", "description": "Reference unique du decaissement", "example": "PAY-2026-0001" },
          "created_at": { "type": "string", "format": "date-time", "example": "2026-03-29T12:00:00Z" }
        }
      },
      "MerchantBalance": {
        "type": "object",
        "properties": {
          "currency": { "type": "string", "description": "Code devise ISO 4217", "example": "XAF" },
          "available_balance": { "type": "integer", "description": "Solde disponible pour les decaissements", "example": 15885960 },
          "pending_balance": { "type": "integer", "description": "Solde en attente de confirmation", "example": 0 }
        }
      },
      "CheckoutSession": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid", "example": "770e8400-e29b-41d4-a716-446655440002" },
          "amount": { "type": "integer", "example": 10000 },
          "currency": { "type": "string", "example": "XAF" },
          "description": { "type": "string", "nullable": true, "example": "Abonnement Premium" },
          "url": { "type": "string", "format": "uri", "example": "https://pay.cowema.org/checkout/770e8400-e29b-41d4-a716-446655440002" },
          "status": { "type": "string", "enum": ["open", "completed", "expired"], "example": "open" },
          "expires_at": { "type": "string", "format": "date-time", "example": "2026-03-29T11:30:00Z" },
          "metadata": { "type": "object", "nullable": true, "example": null },
          "created_at": { "type": "string", "format": "date-time", "example": "2026-03-29T11:00:00Z" }
        }
      },
      "CreatePaymentRequest": {
        "type": "object",
        "required": ["amount", "country", "phone_number", "provider"],
        "properties": {
          "amount": { "type": "integer", "minimum": 1, "description": "Montant en unite minimale", "example": 5000 },
          "country": { "type": "string", "minLength": 2, "maxLength": 2, "description": "Code pays ISO 3166-1 alpha-2. La devise est derivee automatiquement du pays.", "enum": ["CG", "CM", "BJ", "CI", "GH", "GN", "LR", "SZ", "UG", "ZA", "ZM", "CD", "GA", "KE", "MG", "MW", "NE", "NG", "RW", "SC", "TZ", "TD"], "example": "CG" },
          "phone_number": { "type": "string", "maxLength": 20, "description": "Numero de telephone du payeur (format local ou international)", "example": "068510000" },
          "provider": { "type": "string", "enum": ["mtn_momo", "airtel_money"], "description": "Fournisseur de paiement. Doit etre disponible dans le pays choisi.", "example": "mtn_momo" },
          "currency": { "type": "string", "minLength": 3, "maxLength": 3, "nullable": true, "description": "Code devise ISO 4217 (optionnel, derive automatiquement du pays)", "example": "XAF" },
          "metadata": { "type": "object", "nullable": true, "description": "Donnees supplementaires au format cle-valeur", "example": { "order_id": "ORD-123" } }
        }
      },
      "CreatePayoutRequest": {
        "type": "object",
        "required": ["amount", "currency", "country", "provider", "phone_number"],
        "properties": {
          "amount": { "type": "integer", "minimum": 1, "description": "Montant du decaissement en unite minimale", "example": 25000 },
          "currency": { "type": "string", "minLength": 3, "maxLength": 3, "description": "Code devise ISO 4217", "example": "XAF" },
          "country": { "type": "string", "minLength": 2, "maxLength": 2, "description": "Code pays ISO 3166-1 alpha-2", "enum": ["CG", "CM", "BJ", "CI", "GH", "GN", "LR", "SZ", "UG", "ZA", "ZM", "CD", "GA", "KE", "MG", "MW", "NE", "NG", "RW", "SC", "TZ", "TD"], "example": "CG" },
          "provider": { "type": "string", "enum": ["mtn_momo", "airtel_money"], "description": "Fournisseur de paiement", "example": "mtn_momo" },
          "phone_number": { "type": "string", "maxLength": 20, "description": "Numero de telephone du beneficiaire", "example": "068510000" }
        }
      },
      "CreateRefundRequest": {
        "type": "object",
        "required": ["payment_id", "amount"],
        "properties": {
          "payment_id": { "type": "string", "format": "uuid", "description": "UUID du paiement a rembourser", "example": "550e8400-e29b-41d4-a716-446655440000" },
          "amount": { "type": "integer", "minimum": 1, "example": 5000 }
        }
      },
      "CreateCheckoutSessionRequest": {
        "type": "object",
        "required": ["amount", "currency", "success_url", "cancel_url"],
        "properties": {
          "amount": { "type": "integer", "minimum": 1, "example": 10000 },
          "currency": { "type": "string", "minLength": 3, "maxLength": 3, "example": "XAF" },
          "description": { "type": "string", "maxLength": 255, "nullable": true, "example": "Abonnement Premium" },
          "success_url": { "type": "string", "format": "uri", "maxLength": 2048, "example": "https://votresite.com/success" },
          "cancel_url": { "type": "string", "format": "uri", "maxLength": 2048, "example": "https://votresite.com/cancel" },
          "metadata": { "type": "object", "nullable": true, "example": null }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["error", "code"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Message d'erreur lisible",
            "example": "Cle API invalide."
          },
          "code": {
            "type": "string",
            "description": "Code machine de l'erreur",
            "example": "invalid_api_key"
          }
        }
      },
      "ValidationErrorResponse": {
        "type": "object",
        "required": ["message", "errors"],
        "properties": {
          "message": {
            "type": "string",
            "description": "Message d'erreur general",
            "example": "The amount field is required."
          },
          "errors": {
            "type": "object",
            "description": "Detail des erreurs de validation par champ",
            "additionalProperties": {
              "type": "array",
              "items": { "type": "string" }
            },
            "example": {
              "amount": ["The amount field is required."],
              "currency": ["The currency field must be 3 characters."]
            }
          }
        }
      },
      "PaginationLinks": {
        "type": "object",
        "properties": {
          "first": { "type": "string", "format": "uri", "nullable": true, "example": "https://pay.cowema.org/api/v1/payments?page=1" },
          "last": { "type": "string", "format": "uri", "nullable": true, "example": "https://pay.cowema.org/api/v1/payments?page=5" },
          "prev": { "type": "string", "format": "uri", "nullable": true, "example": null },
          "next": { "type": "string", "format": "uri", "nullable": true, "example": "https://pay.cowema.org/api/v1/payments?page=2" }
        }
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "current_page": { "type": "integer", "example": 1 },
          "from": { "type": "integer", "nullable": true, "example": 1 },
          "last_page": { "type": "integer", "example": 5 },
          "per_page": { "type": "integer", "example": 20 },
          "to": { "type": "integer", "nullable": true, "example": 20 },
          "total": { "type": "integer", "example": 95 }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Non authentifie -- cle API manquante ou invalide",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "missing_api_key": {
                "summary": "Cle API manquante",
                "value": {
                  "error": "Cle API manquante.",
                  "code": "missing_api_key"
                }
              },
              "invalid_api_key": {
                "summary": "Cle API invalide",
                "value": {
                  "error": "Cle API invalide.",
                  "code": "invalid_api_key"
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Acces refuse -- IP non autorisee ou verification KYC requise",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "ip_not_whitelisted": {
                "summary": "Adresse IP non autorisee",
                "value": {
                  "error": "Acces refuse depuis cette adresse IP.",
                  "code": "ip_not_whitelisted"
                }
              },
              "kyc_verification_required": {
                "summary": "Verification KYC requise",
                "value": {
                  "error": "Verification KYC requise pour le mode production.",
                  "code": "kyc_verification_required"
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Ressource introuvable",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "payment_not_found": {
                "summary": "Paiement introuvable",
                "value": {
                  "error": "Transaction introuvable.",
                  "code": "not_found"
                }
              },
              "refund_not_found": {
                "summary": "Remboursement introuvable",
                "value": {
                  "error": "Remboursement introuvable.",
                  "code": "not_found"
                }
              },
              "checkout_session_not_found": {
                "summary": "Session checkout introuvable",
                "value": {
                  "error": "Session de paiement introuvable.",
                  "code": "not_found"
                }
              },
              "payout_not_found": {
                "summary": "Decaissement introuvable",
                "value": {
                  "error": "Decaissement introuvable.",
                  "code": "not_found"
                }
              }
            }
          }
        }
      },
      "ValidationError": {
        "description": "Erreur de validation des donnees envoyees",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationErrorResponse"
            },
            "example": {
              "message": "The amount field is required.",
              "errors": {
                "amount": ["The amount field is required."],
                "currency": ["The currency field must be 3 characters."]
              }
            }
          }
        }
      },
      "RefundValidationError": {
        "description": "Erreur de validation ou paiement non remboursable",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                { "$ref": "#/components/schemas/ValidationErrorResponse" },
                { "$ref": "#/components/schemas/ErrorResponse" }
              ]
            },
            "examples": {
              "validation_error": {
                "summary": "Erreur de validation",
                "value": {
                  "message": "The payment id field must be a valid UUID.",
                  "errors": {
                    "payment_id": ["The payment id field must be a valid UUID."],
                    "amount": ["The amount field must be at least 1."]
                  }
                }
              },
              "not_refundable": {
                "summary": "Transaction non remboursable",
                "value": {
                  "error": "Transaction originale introuvable ou non remboursable.",
                  "code": "not_refundable"
                }
              }
            }
          }
        }
      },
      "IdempotencyConflict": {
        "description": "Cle d'idempotence deja utilisee avec un corps de requete different",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Idempotency key already used with different request.",
              "code": "idempotency_conflict"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Trop de requetes -- limite de debit atteinte",
        "headers": {
          "X-RateLimit-Limit": {
            "description": "Nombre maximum de requetes autorisees par minute",
            "schema": { "type": "integer", "example": 100 }
          },
          "X-RateLimit-Remaining": {
            "description": "Nombre de requetes restantes dans la fenetre courante",
            "schema": { "type": "integer", "example": 0 }
          },
          "Retry-After": {
            "description": "Nombre de secondes avant la prochaine fenetre",
            "schema": { "type": "integer", "example": 30 }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "example": "Too Many Attempts."
                }
              }
            },
            "example": {
              "message": "Too Many Attempts."
            }
          }
        }
      }
    }
  }
}
