openapi: 3.1.0
info:
  title: Paymee Merchant API
  version: 1.0.0
  description: |-
    Server-to-server API for integrating the Paymee payment gateway.

    Authenticate every request with your **merchant secret API key** in the `Authorization` header. The **key prefix selects the mode** on the same host: `sk_test_...` for test mode, `sk_live_...` for live mode.

    Typical flow: create a payment session, redirect the buyer to the returned `checkout_url`, then track the outcome via webhooks and the session endpoints.

    Errors use RFC 9457 problem details (`application/problem+json`).

    **Download** this specification (OpenAPI 3.1, YAML) to generate a client or import it into Postman: [merchant-api.yaml](https://docs.paymee.online/openapi/merchant-api.yaml).

    **Webhooks** are listed below the operations. Each delivery is a `POST` of the event as JSON, signed in the `X-Paymee-Signature` header (`t=<timestamp>,v1=<hex>`; two `v1` values for 24 hours after a secret rotation), with the event id in `X-Paymee-Webhook-Id` and its name in `X-Paymee-Event`.
  contact:
    name: Paymee support
    email: support@paymee.tn
paths:
  /gateway/sessions/:
    post:
      operationId: createPaymentSession
      description: |-
        Create a payment session and its initial hosted checkout. The response includes a `checkout_url` to redirect the buyer to. Reusing the same `merchant_reference` returns the existing session (`200`) instead of creating a duplicate.

        **Currency.** The buyer's card is always charged, and you are always settled, in your account's currency (TND). A request in TND is charged as sent. A request in another currency is converted at the exchange rate you configured for it (Settings → Currencies), rounded half-up to millimes; the response `amount`/`currency` are then the TND charge and `original` carries what you requested plus the rate applied. A request in a currency you have not configured a rate for is rejected with `422` and `errors[].code = "currency_unsupported"` on the `currency` field. The rate is frozen on the session at creation; later rate changes never alter an existing session, and an idempotent replay returns the original snapshot.
      summary: Create a payment session
      tags:
      - Sessions
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSessionRequest'
            examples:
              CreateASession:
                value:
                  amount: '10.500'
                  currency: TND
                  merchant_reference: order-0001
                  first_name: Aymen
                  last_name: Ben Salah
                  email: aymen@example.com
                  return_url: https://your-site.tn/payment/success
                  cancel_url: https://your-site.tn/payment/cancel
                  webhook_url: https://your-site.tn/webhooks/paymee
                  metadata:
                    cart_id: '8812'
                summary: Create a session
        required: true
      security:
      - MerchantApiKey: []
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionDetail'
          description: ''
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionDetail'
          description: ''
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: 'Malformed request: the body is not valid JSON, or a header
            is invalid.'
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Missing, malformed, expired, or revoked API key.
        '409':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ConflictProblem'
          description: The request conflicts with an earlier one; the `type` says
            how.
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ValidationProblem'
          description: A field failed validation (`errors` lists them), or a business
            rule refused the request (the `type` says which).
        '429':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Rate limit exceeded. Wait the number of seconds in the `Retry-After`
            header.
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Unexpected error; nothing was changed. Safe to retry with backoff.
  /gateway/sessions/list/:
    get:
      operationId: listPaymentSessions
      description: List the merchant account's payment sessions, newest first, with
        cursor pagination.
      summary: List payment sessions
      parameters:
      - in: query
        name: ending_before
        schema:
          type: string
        description: Return sessions created after this session id (backward pagination).
      - in: query
        name: limit
        schema:
          type: integer
        description: Page size (default 20, max 100).
      - in: query
        name: starting_after
        schema:
          type: string
        description: Return sessions created before this session id (forward pagination).
      tags:
      - Sessions
      security:
      - MerchantApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionListResponse'
          description: ''
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Missing, malformed, expired, or revoked API key.
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ValidationProblem'
          description: A field failed validation (`errors` lists them), or a business
            rule refused the request (the `type` says which).
        '429':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Rate limit exceeded. Wait the number of seconds in the `Retry-After`
            header.
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Unexpected error; nothing was changed. Safe to retry with backoff.
  /gateway/sessions/{session_id}/:
    get:
      operationId: getPaymentSession
      description: Retrieve a payment session, including its attempt history.
      summary: Retrieve a payment session
      parameters:
      - in: path
        name: session_id
        schema:
          type: string
        description: Prefixed public id of the payment session (e.g. `ps_...`).
        required: true
      tags:
      - Sessions
      security:
      - MerchantApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionDetail'
          description: ''
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Missing, malformed, expired, or revoked API key.
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: The payment session does not exist for this merchant account.
        '429':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Rate limit exceeded. Wait the number of seconds in the `Retry-After`
            header.
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Unexpected error; nothing was changed. Safe to retry with backoff.
  /gateway/sessions/{session_id}/cancel/:
    post:
      operationId: cancelPaymentSession
      description: Cancel a payment session that has not reached a terminal state.
      summary: Cancel a payment session
      parameters:
      - in: path
        name: session_id
        schema:
          type: string
        description: Prefixed public id of the payment session (e.g. `ps_...`).
        required: true
      tags:
      - Sessions
      security:
      - MerchantApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionDetail'
          description: ''
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Missing, malformed, expired, or revoked API key.
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: The payment session does not exist for this merchant account.
        '409':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ConflictProblem'
          description: The request conflicts with an earlier one; the `type` says
            how.
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ValidationProblem'
          description: A field failed validation (`errors` lists them), or a business
            rule refused the request (the `type` says which).
        '429':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Rate limit exceeded. Wait the number of seconds in the `Retry-After`
            header.
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Unexpected error; nothing was changed. Safe to retry with backoff.
  /gateway/sessions/{session_id}/refund/:
    post:
      operationId: refundPaymentSession
      description: 'Issue a full or partial refund against a succeeded payment session.
        For `PARTIAL` refunds, `amount` is required. The response is the refund: `SUCCEEDED`
        when the acquirer confirmed it, `PENDING` when it is completed manually (`refund.succeeded`
        / `refund.failed` follow). Send an `Idempotency-Key` to retry safely.'
      summary: Refund a payment session
      parameters:
      - in: header
        name: Idempotency-Key
        schema:
          type: string
        description: 'A unique value you generate per refund (e.g. a UUID v4), up
          to 255 characters. Repeating a request with the same key returns the refund
          it created (header `Idempotent-Replayed: true`) instead of refunding again;
          reusing it for a different request is a `409 idempotency-key-reuse`. Keys
          do not expire.'
      - in: path
        name: session_id
        schema:
          type: string
        description: Prefixed public id of the payment session (e.g. `ps_...`).
        required: true
      tags:
      - Refunds
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MerchantRefundRequestRequest'
            examples:
              FullRefund:
                value:
                  reason: Customer requested a refund
                  refund_type: FULL
                summary: Full refund
              PartialRefund:
                value:
                  reason: Damaged item
                  refund_type: PARTIAL
                  amount: '3.000'
                summary: Partial refund
        required: true
      security:
      - MerchantApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Refund'
          description: ''
        '400':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: 'Malformed request: the body is not valid JSON, or a header
            is invalid.'
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Missing, malformed, expired, or revoked API key.
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: The payment session does not exist for this merchant account.
        '409':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ConflictProblem'
          description: The request conflicts with an earlier one; the `type` says
            how.
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ValidationProblem'
          description: A field failed validation (`errors` lists them), or a business
            rule refused the request (the `type` says which).
        '429':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Rate limit exceeded. Wait the number of seconds in the `Retry-After`
            header.
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Unexpected error; nothing was changed. Safe to retry with backoff.
  /gateway/sessions/{session_id}/webhooks/:
    get:
      operationId: listSessionWebhookDeliveries
      description: List the most recent webhook delivery attempts for a single payment
        session.
      summary: List webhook deliveries for a session
      parameters:
      - in: path
        name: session_id
        schema:
          type: string
        description: Prefixed public id of the payment session (e.g. `ps_...`).
        required: true
      tags:
      - Webhook deliveries
      security:
      - MerchantApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionWebhookListResponse'
          description: ''
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Missing, malformed, expired, or revoked API key.
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: The payment session does not exist for this merchant account.
        '429':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Rate limit exceeded. Wait the number of seconds in the `Retry-After`
            header.
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Unexpected error; nothing was changed. Safe to retry with backoff.
  /gateway/webhooks/:
    get:
      operationId: listWebhookDeliveries
      description: List the merchant account's webhook delivery attempts, newest first,
        with cursor pagination.
      summary: List webhook deliveries
      parameters:
      - in: query
        name: limit
        schema:
          type: integer
        description: Page size (default 20, max 100).
      - in: query
        name: starting_after
        schema:
          type: string
        description: Return deliveries created before this delivery id (forward pagination).
      tags:
      - Webhook deliveries
      security:
      - MerchantApiKey: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookListResponse'
          description: ''
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Missing, malformed, expired, or revoked API key.
        '422':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ValidationProblem'
          description: A field failed validation (`errors` lists them), or a business
            rule refused the request (the `type` says which).
        '429':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Rate limit exceeded. Wait the number of seconds in the `Retry-After`
            header.
        '500':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetail'
          description: Unexpected error; nothing was changed. Safe to retry with backoff.
components:
  schemas:
    Attempt:
      type: object
      description: Serializer for payment attempts (read-only).
      properties:
        id:
          type: string
          readOnly: true
        attempt_number:
          type: integer
          maximum: 2147483647
          minimum: 0
        status:
          enum:
          - INITIATED
          - PROCESSING
          - SUCCEEDED
          - FAILED
          - ABANDONED
          type: string
          description: |-
            * `INITIATED` - Initiated
            * `PROCESSING` - Processing
            * `SUCCEEDED` - Succeeded
            * `FAILED` - Failed
            * `ABANDONED` - Abandoned
          x-spec-enum-id: 1d209af02734b51b
        card_brand:
          type:
          - string
          - 'null'
          readOnly: true
        card_last_four:
          type:
          - string
          - 'null'
          readOnly: true
        failure_code:
          type:
          - string
          - 'null'
          description: The acquirer's decline code, as webhooks carry it; None unless
            the attempt failed.
          readOnly: true
        failure_message:
          type:
          - string
          - 'null'
          description: The acquirer's decline reason, as webhooks carry it; None unless
            the attempt failed.
          readOnly: true
        fee_amount:
          type:
          - string
          - 'null'
          readOnly: true
        net_amount:
          type:
          - string
          - 'null'
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
      required:
      - attempt_number
      - card_brand
      - card_last_four
      - created_at
      - failure_code
      - failure_message
      - fee_amount
      - id
      - net_amount
    CheckoutSessionExpiredEvent:
      type: object
      description: The `checkout.session.expired` event.
      properties:
        id:
          type: string
          description: Event id (`evt_…`), stable across retries; also the `X-Paymee-Webhook-Id`
            header.
        event:
          enum:
          - checkout.session.expired
          type: string
          description: '* `checkout.session.expired` - checkout.session.expired'
          x-spec-enum-id: dd10cb66bd4a5670
        created_at:
          type: string
          format: date-time
        livemode:
          type: boolean
          description: False for events from test mode (`sk_test_` keys).
        data:
          $ref: '#/components/schemas/WebhookCheckoutExpired'
      required:
      - created_at
      - data
      - event
      - id
      - livemode
    ConflictProblem:
      type: object
      description: Problem detail of a 409.
      properties:
        type:
          type: string
          format: uri
          description: URI identifying the error type.
        title:
          type: string
          description: Short, human-readable summary of the error type.
        status:
          type: integer
          description: HTTP status code.
        detail:
          type: string
          description: Human-readable explanation specific to this occurrence.
        existing_session_id:
          type: string
          description: 'On a `conflict` from session creation: the session that already
            uses this `merchant_reference`.'
      required:
      - detail
      - status
      - title
      - type
    CreateSessionRequest:
      type: object
      description: |-
        Input serializer for creating a payment session.

        Optional strings accept `""` as "no value" (as Stripe does), so a client that sends
        every field of a form, filled or not, is not refused.
      properties:
        amount:
          type: string
          minLength: 1
          description: Amount as string with correct decimal places for currency
        currency:
          type: string
          description: ISO 4217 code. Defaults to the account's currency when omitted.
          maxLength: 3
        merchant_reference:
          type: string
          minLength: 1
          maxLength: 200
        description:
          type: string
          default: ''
          maxLength: 500
        order_id:
          type: string
          default: ''
          description: Merchant's display-friendly order ID
          maxLength: 100
        first_name:
          type: string
          minLength: 1
          maxLength: 100
        last_name:
          type: string
          minLength: 1
          maxLength: 100
        email:
          type: string
          format: email
          minLength: 1
          maxLength: 255
        phone:
          type: string
          default: ''
          maxLength: 20
        items:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: 'Line items: [{name, quantity, unit_price, description}]'
        metadata: {}
        return_url:
          type: string
          default: ''
          maxLength: 2048
        cancel_url:
          type: string
          default: ''
          maxLength: 2048
        webhook_url:
          type: string
          default: ''
          maxLength: 2048
        max_attempts:
          type: integer
          maximum: 10
          minimum: 1
          default: 5
        locale:
          enum:
          - auto
          - fr
          - en
          type: string
          x-spec-enum-id: e1cf3ea2451b87a8
          default: auto
          description: |-
            Language of the payment page: `fr`, `en`, or `auto` (the default) to follow the buyer's browser. The buyer can still switch language on the page. The card-entry page opens in the language the buyer is reading when they pay.

            * `auto` - The buyer's browser language
            * `fr` - French
            * `en` - English
      required:
      - amount
      - email
      - first_name
      - last_name
      - merchant_reference
    MerchantRefundRequestRequest:
      type: object
      description: |-
        A refund request on the merchant API (`sk_` keys).

        `reopen_invoice` is a merchant-portal option (invoices are a portal feature), so the
        API neither takes nor documents it.
      properties:
        reason:
          type: string
          minLength: 1
          maxLength: 500
        refund_type:
          enum:
          - FULL
          - PARTIAL
          type: string
          description: |-
            * `FULL` - FULL
            * `PARTIAL` - PARTIAL
          x-spec-enum-id: dc996ef0b4e22174
          default: FULL
        amount:
          type: string
          minLength: 1
          description: Required for partial refunds. A positive decimal string in
            the payment's currency, e.g. "3.000".
        metadata: {}
      required:
      - reason
    OriginalAmount:
      type: object
      description: |-
        What the merchant requested, frozen at creation, and the rate applied.

        `amount`/`currency` on the session are always the platform-currency charge (TND);
        this object carries the request. `rate` is `"1.000000"` when no conversion applied.
        Declared as a serializer (rather than a method field) so the merchant OpenAPI
        documents its shape; the values come from `PaymentSession.original_snapshot`.
      properties:
        amount:
          type: string
          readOnly: true
          description: Requested amount, as a decimal string at the requested currency's
            precision.
        currency:
          type: string
          readOnly: true
          description: Requested ISO 4217 currency code.
        rate:
          type: string
          readOnly: true
          description: Exchange rate applied (1 original.currency = rate currency),
            six decimals. "1.000000" when no conversion.
      required:
      - amount
      - currency
      - rate
    ProblemDetail:
      type: object
      description: RFC 9457 problem detail body (``application/problem+json``).
      properties:
        type:
          type: string
          format: uri
          description: URI identifying the error type.
        title:
          type: string
          description: Short, human-readable summary of the error type.
        status:
          type: integer
          description: HTTP status code.
        detail:
          type: string
          description: Human-readable explanation specific to this occurrence.
      required:
      - detail
      - status
      - title
      - type
    Refund:
      type: object
      description: A refund.
      properties:
        id:
          type: string
          readOnly: true
          description: Refund id (`re_…`).
        session_id:
          type: string
          readOnly: true
          description: The refunded payment session (`ps_…`).
        merchant_reference:
          type: string
          readOnly: true
          description: The session's `merchant_reference`.
        amount:
          type: string
          readOnly: true
          description: Refunded amount, as a decimal string.
        currency:
          type: string
          readOnly: true
          description: ISO 4217 currency of `amount`.
        refund_type:
          enum:
          - FULL
          - PARTIAL
          type: string
          description: |-
            * `FULL` - Full
            * `PARTIAL` - Partial
          x-spec-enum-id: cf228c963376e6ba
        status:
          enum:
          - PENDING
          - SUCCEEDED
          - FAILED
          type: string
          description: '`PENDING` while the refund is in progress, `SUCCEEDED` once
            the money is returned, `FAILED` if it was not.'
          x-spec-enum-id: ea6831efbceadc85
          readOnly: true
        reason:
          type: string
          title: Refund reason
        processor_message:
          type:
          - string
          - 'null'
          readOnly: true
          description: Why the refund is pending or failed, when known. Informational;
            may change.
        created_at:
          type: string
          format: date-time
          readOnly: true
        completed_at:
          type:
          - string
          - 'null'
          readOnly: true
          description: When the refund reached `SUCCEEDED` or `FAILED`.
      required:
      - amount
      - completed_at
      - created_at
      - currency
      - id
      - merchant_reference
      - processor_message
      - session_id
      - status
    RefundFailedEvent:
      type: object
      description: The `refund.failed` event.
      properties:
        id:
          type: string
          description: Event id (`evt_…`), stable across retries; also the `X-Paymee-Webhook-Id`
            header.
        event:
          enum:
          - refund.failed
          type: string
          description: '* `refund.failed` - refund.failed'
          x-spec-enum-id: 775f6589cbea8599
        created_at:
          type: string
          format: date-time
        livemode:
          type: boolean
          description: False for events from test mode (`sk_test_` keys).
        data:
          $ref: '#/components/schemas/Refund'
      required:
      - created_at
      - data
      - event
      - id
      - livemode
    RefundSucceededEvent:
      type: object
      description: The `refund.succeeded` event.
      properties:
        id:
          type: string
          description: Event id (`evt_…`), stable across retries; also the `X-Paymee-Webhook-Id`
            header.
        event:
          enum:
          - refund.succeeded
          type: string
          description: '* `refund.succeeded` - refund.succeeded'
          x-spec-enum-id: 174790c1f7c7e0a7
        created_at:
          type: string
          format: date-time
        livemode:
          type: boolean
          description: False for events from test mode (`sk_test_` keys).
        data:
          $ref: '#/components/schemas/Refund'
      required:
      - created_at
      - data
      - event
      - id
      - livemode
    SessionAttemptFailedEvent:
      type: object
      description: The `session.attempt_failed` event.
      properties:
        id:
          type: string
          description: Event id (`evt_…`), stable across retries; also the `X-Paymee-Webhook-Id`
            header.
        event:
          enum:
          - session.attempt_failed
          type: string
          description: '* `session.attempt_failed` - session.attempt_failed'
          x-spec-enum-id: 9e134a386e158dc9
        created_at:
          type: string
          format: date-time
        livemode:
          type: boolean
          description: False for events from test mode (`sk_test_` keys).
        data:
          $ref: '#/components/schemas/WebhookSessionAttemptFailed'
      required:
      - created_at
      - data
      - event
      - id
      - livemode
    SessionBuyer:
      type: object
      description: The buyer (the same object as a webhook's `data.buyer`).
      properties:
        first_name:
          type: string
          readOnly: true
        last_name:
          type: string
          readOnly: true
        email:
          type: string
          readOnly: true
        phone:
          type: string
          readOnly: true
      required:
      - email
      - first_name
      - last_name
      - phone
    SessionDetail:
      type: object
      description: Response serializer for payment session detail.
      properties:
        id:
          type: string
          readOnly: true
        merchant_reference:
          type: string
          description: Merchant-provided unique reference for idempotency
          maxLength: 200
        order_number:
          type: string
          description: System-generated order number (PM-XXXXXX)
          maxLength: 10
        order_id:
          type: string
          title: Merchant order ID
          description: Merchant's display-friendly order ID (separate from merchant_reference)
          maxLength: 100
        amount:
          type: string
          readOnly: true
        currency:
          type: string
          maxLength: 3
        original:
          allOf:
          - $ref: '#/components/schemas/OriginalAmount'
          readOnly: true
        description:
          type: string
          maxLength: 500
        items:
          description: 'Line items: [{name, quantity, unit_price, description}]'
        status:
          enum:
          - CREATED
          - PENDING
          - PROCESSING
          - SUCCEEDED
          - FAILED
          - EXPIRED
          - CANCELED
          type: string
          description: |-
            * `CREATED` - Created
            * `PENDING` - Pending
            * `PROCESSING` - Processing
            * `SUCCEEDED` - Succeeded
            * `FAILED` - Failed
            * `EXPIRED` - Expired
            * `CANCELED` - Canceled
          x-spec-enum-id: e80429814b84068c
        livemode:
          type: boolean
          readOnly: true
        buyer:
          allOf:
          - $ref: '#/components/schemas/SessionBuyer'
          readOnly: true
        metadata: {}
        return_url:
          oneOf:
          - type: string
            format: uri
            maxLength: 2048
          - type: string
            maxLength: 0
        cancel_url:
          description: Redirect buyer here on payment failure or cancellation
          oneOf:
          - type: string
            format: uri
            maxLength: 2048
          - type: string
            maxLength: 0
        webhook_url:
          description: Per-session webhook URL override
          oneOf:
          - type: string
            format: uri
            maxLength: 2048
          - type: string
            maxLength: 0
        max_attempts:
          type: integer
          maximum: 2147483647
          minimum: 0
        checkout_url:
          type:
          - string
          - 'null'
          readOnly: true
        locale:
          type: string
          enum:
          - auto
          - fr
          - en
          readOnly: true
          description: 'Language of the payment page: `fr`, `en`, or `auto` (the buyer''s
            browser).'
        expires_at:
          type: string
          format: date-time
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
          readOnly: true
        attempts:
          type: array
          items:
            $ref: '#/components/schemas/Attempt'
          readOnly: true
        refunds:
          type: array
          items:
            $ref: '#/components/schemas/Refund'
          readOnly: true
          description: Every refund of this session, oldest first.
      required:
      - amount
      - attempts
      - buyer
      - checkout_url
      - created_at
      - expires_at
      - id
      - livemode
      - locale
      - merchant_reference
      - order_number
      - original
      - refunds
    SessionExpiredEvent:
      type: object
      description: The `session.expired` event.
      properties:
        id:
          type: string
          description: Event id (`evt_…`), stable across retries; also the `X-Paymee-Webhook-Id`
            header.
        event:
          enum:
          - session.expired
          type: string
          description: '* `session.expired` - session.expired'
          x-spec-enum-id: 6cb2153fe299ab2f
        created_at:
          type: string
          format: date-time
        livemode:
          type: boolean
          description: False for events from test mode (`sk_test_` keys).
        data:
          $ref: '#/components/schemas/WebhookSession'
      required:
      - created_at
      - data
      - event
      - id
      - livemode
    SessionFailedEvent:
      type: object
      description: The `session.failed` event.
      properties:
        id:
          type: string
          description: Event id (`evt_…`), stable across retries; also the `X-Paymee-Webhook-Id`
            header.
        event:
          enum:
          - session.failed
          type: string
          description: '* `session.failed` - session.failed'
          x-spec-enum-id: ba77bfd2aed0a18c
        created_at:
          type: string
          format: date-time
        livemode:
          type: boolean
          description: False for events from test mode (`sk_test_` keys).
        data:
          $ref: '#/components/schemas/WebhookSessionFailed'
      required:
      - created_at
      - data
      - event
      - id
      - livemode
    SessionList:
      type: object
      description: Lightweight serializer for session list endpoint.
      properties:
        id:
          type: string
          readOnly: true
        merchant_reference:
          type: string
          description: Merchant-provided unique reference for idempotency
          maxLength: 200
        order_number:
          type: string
          description: System-generated order number (PM-XXXXXX)
          maxLength: 10
        order_id:
          type: string
          title: Merchant order ID
          description: Merchant's display-friendly order ID (separate from merchant_reference)
          maxLength: 100
        amount:
          type: string
          readOnly: true
        currency:
          type: string
          maxLength: 3
        original:
          allOf:
          - $ref: '#/components/schemas/OriginalAmount'
          readOnly: true
        status:
          enum:
          - CREATED
          - PENDING
          - PROCESSING
          - SUCCEEDED
          - FAILED
          - EXPIRED
          - CANCELED
          type: string
          description: |-
            * `CREATED` - Created
            * `PENDING` - Pending
            * `PROCESSING` - Processing
            * `SUCCEEDED` - Succeeded
            * `FAILED` - Failed
            * `EXPIRED` - Expired
            * `CANCELED` - Canceled
          x-spec-enum-id: e80429814b84068c
        livemode:
          type: boolean
          readOnly: true
        buyer:
          allOf:
          - $ref: '#/components/schemas/SessionBuyer'
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
      required:
      - amount
      - buyer
      - created_at
      - id
      - livemode
      - merchant_reference
      - order_number
      - original
    SessionListResponse:
      type: object
      description: Cursor-paginated list of payment sessions.
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/SessionList'
        has_more:
          type: boolean
          description: True when more results exist past this page (use the last id
            as `starting_after`).
      required:
      - data
      - has_more
    SessionSucceededEvent:
      type: object
      description: The `session.succeeded` event.
      properties:
        id:
          type: string
          description: Event id (`evt_…`), stable across retries; also the `X-Paymee-Webhook-Id`
            header.
        event:
          enum:
          - session.succeeded
          type: string
          description: '* `session.succeeded` - session.succeeded'
          x-spec-enum-id: c01cad88f25a45c1
        created_at:
          type: string
          format: date-time
        livemode:
          type: boolean
          description: False for events from test mode (`sk_test_` keys).
        data:
          $ref: '#/components/schemas/WebhookSessionSucceeded'
      required:
      - created_at
      - data
      - event
      - id
      - livemode
    SessionWebhookListResponse:
      type: object
      description: Webhook deliveries for a single payment session (most recent first).
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/WebhookDelivery'
      required:
      - data
    ValidationErrorItem:
      type: object
      description: A single field-level validation error inside a problem+json body.
      properties:
        field:
          type: string
          description: Name of the field that failed validation.
        code:
          type: string
          description: Machine-readable error code, e.g. 'required'.
        message:
          type: string
          description: Human-readable description of the failure.
      required:
      - code
      - field
      - message
    ValidationProblem:
      type: object
      description: |-
        Problem detail of a 422: a per-field ``errors`` array when fields failed validation;
        no ``errors`` when a business rule refused the request (the ``type`` says which).
      properties:
        type:
          type: string
          format: uri
          description: URI identifying the error type.
        title:
          type: string
          description: Short, human-readable summary of the error type.
        status:
          type: integer
          description: HTTP status code.
        detail:
          type: string
          description: Human-readable explanation specific to this occurrence.
        errors:
          type: array
          items:
            $ref: '#/components/schemas/ValidationErrorItem'
      required:
      - detail
      - status
      - title
      - type
    WebhookAttempt:
      type: object
      properties:
        id:
          type: string
          description: Payment attempt id (`pa_…`).
        attempt_number:
          type: integer
          description: 1 for the first attempt.
        failure_code:
          type: string
          description: The acquirer's decline code. Informational; may change.
        failure_message:
          type: string
          description: The acquirer's decline reason. Informational; may change.
      required:
      - attempt_number
      - failure_code
      - failure_message
      - id
    WebhookBuyer:
      type: object
      properties:
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
        phone:
          type: string
          description: Empty when not given.
      required:
      - email
      - first_name
      - last_name
      - phone
    WebhookCheckoutExpired:
      type: object
      description: The payment session, as every `session.*` and `checkout.session.expired`
        event carries it.
      properties:
        id:
          type: string
          description: Payment session id (`ps_…`).
        order_number:
          type: string
          description: Paymee's order number (`PM-…`).
        merchant_reference:
          type: string
        order_id:
          type: string
          description: Empty when not given.
        amount:
          type: string
          description: Charged amount, decimal string (TND, 3 decimals).
        currency:
          type: string
        original:
          allOf:
          - $ref: '#/components/schemas/OriginalAmount'
          description: What you requested, and the exchange rate applied.
        description:
          type: string
        status:
          enum:
          - CREATED
          - PENDING
          - PROCESSING
          - SUCCEEDED
          - FAILED
          - EXPIRED
          - CANCELED
          type: string
          description: |-
            * `CREATED` - CREATED
            * `PENDING` - PENDING
            * `PROCESSING` - PROCESSING
            * `SUCCEEDED` - SUCCEEDED
            * `FAILED` - FAILED
            * `EXPIRED` - EXPIRED
            * `CANCELED` - CANCELED
          x-spec-enum-id: cb62bfcc2231b12c
        buyer:
          $ref: '#/components/schemas/WebhookBuyer'
        items:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: The line items you sent, as sent.
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Your metadata, values as strings.
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
          description: When the session reached a final status; null while it is open.
        checkout_session_token:
          type: string
          description: A short prefix of the expired link's token.
      required:
      - amount
      - buyer
      - checkout_session_token
      - completed_at
      - currency
      - description
      - id
      - items
      - merchant_reference
      - metadata
      - order_id
      - order_number
      - original
      - status
    WebhookDelivery:
      type: object
      description: Serializer for merchant webhook delivery history.
      properties:
        id:
          type: string
          readOnly: true
        session_id:
          type: string
          readOnly: true
        event:
          type: string
          title: Event type
          description: e.g. session.succeeded, session.failed
          maxLength: 50
        event_id:
          type: string
          description: Stable ID shared across retries for merchant deduplication
          maxLength: 64
        url:
          type: string
          format: uri
          title: Webhook URL
          maxLength: 2048
        attempt_number:
          type: integer
          maximum: 2147483647
          minimum: 0
        status:
          enum:
          - PENDING
          - SUCCEEDED
          - FAILED
          type: string
          description: |-
            * `PENDING` - Pending
            * `SUCCEEDED` - Succeeded
            * `FAILED` - Failed
          x-spec-enum-id: d0a8199f26b02615
        http_status_code:
          type:
          - integer
          - 'null'
          maximum: 2147483647
          minimum: 0
        response_time_ms:
          type:
          - integer
          - 'null'
          maximum: 2147483647
          minimum: 0
          title: Response time (ms)
        error_message:
          type: string
        next_retry_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Scheduled time for next retry attempt
        created_at:
          type: string
          format: date-time
          readOnly: true
      required:
      - created_at
      - event
      - id
      - session_id
      - url
    WebhookListResponse:
      type: object
      description: Cursor-paginated list of webhook deliveries for the account.
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/WebhookDelivery'
        has_more:
          type: boolean
          description: True when more results exist past this page (use the last id
            as `starting_after`).
      required:
      - data
      - has_more
    WebhookPayment:
      type: object
      properties:
        method:
          type: string
          description: '`CARD`.'
        card_brand:
          type: string
        card_last_four:
          type: string
        card_country:
          type: string
          description: The issuing bank's country name.
        fee_amount:
          type: string
        net_amount:
          type: string
      required:
      - card_brand
      - card_country
      - card_last_four
      - fee_amount
      - method
      - net_amount
    WebhookSession:
      type: object
      description: The payment session, as every `session.*` and `checkout.session.expired`
        event carries it.
      properties:
        id:
          type: string
          description: Payment session id (`ps_…`).
        order_number:
          type: string
          description: Paymee's order number (`PM-…`).
        merchant_reference:
          type: string
        order_id:
          type: string
          description: Empty when not given.
        amount:
          type: string
          description: Charged amount, decimal string (TND, 3 decimals).
        currency:
          type: string
        original:
          allOf:
          - $ref: '#/components/schemas/OriginalAmount'
          description: What you requested, and the exchange rate applied.
        description:
          type: string
        status:
          enum:
          - CREATED
          - PENDING
          - PROCESSING
          - SUCCEEDED
          - FAILED
          - EXPIRED
          - CANCELED
          type: string
          description: |-
            * `CREATED` - CREATED
            * `PENDING` - PENDING
            * `PROCESSING` - PROCESSING
            * `SUCCEEDED` - SUCCEEDED
            * `FAILED` - FAILED
            * `EXPIRED` - EXPIRED
            * `CANCELED` - CANCELED
          x-spec-enum-id: cb62bfcc2231b12c
        buyer:
          $ref: '#/components/schemas/WebhookBuyer'
        items:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: The line items you sent, as sent.
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Your metadata, values as strings.
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
          description: When the session reached a final status; null while it is open.
      required:
      - amount
      - buyer
      - completed_at
      - currency
      - description
      - id
      - items
      - merchant_reference
      - metadata
      - order_id
      - order_number
      - original
      - status
    WebhookSessionAttemptFailed:
      type: object
      description: The payment session, as every `session.*` and `checkout.session.expired`
        event carries it.
      properties:
        id:
          type: string
          description: Payment session id (`ps_…`).
        order_number:
          type: string
          description: Paymee's order number (`PM-…`).
        merchant_reference:
          type: string
        order_id:
          type: string
          description: Empty when not given.
        amount:
          type: string
          description: Charged amount, decimal string (TND, 3 decimals).
        currency:
          type: string
        original:
          allOf:
          - $ref: '#/components/schemas/OriginalAmount'
          description: What you requested, and the exchange rate applied.
        description:
          type: string
        status:
          enum:
          - CREATED
          - PENDING
          - PROCESSING
          - SUCCEEDED
          - FAILED
          - EXPIRED
          - CANCELED
          type: string
          description: |-
            * `CREATED` - CREATED
            * `PENDING` - PENDING
            * `PROCESSING` - PROCESSING
            * `SUCCEEDED` - SUCCEEDED
            * `FAILED` - FAILED
            * `EXPIRED` - EXPIRED
            * `CANCELED` - CANCELED
          x-spec-enum-id: cb62bfcc2231b12c
        buyer:
          $ref: '#/components/schemas/WebhookBuyer'
        items:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: The line items you sent, as sent.
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Your metadata, values as strings.
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
          description: When the session reached a final status; null while it is open.
        attempt:
          $ref: '#/components/schemas/WebhookAttempt'
        attempts_count:
          type: integer
          description: Attempts made so far, this one included.
        attempts_remaining:
          type: integer
        can_retry:
          type: boolean
          description: True while the buyer can try again on this session.
      required:
      - amount
      - attempt
      - attempts_count
      - attempts_remaining
      - buyer
      - can_retry
      - completed_at
      - currency
      - description
      - id
      - items
      - merchant_reference
      - metadata
      - order_id
      - order_number
      - original
      - status
    WebhookSessionFailed:
      type: object
      description: The payment session, as every `session.*` and `checkout.session.expired`
        event carries it.
      properties:
        id:
          type: string
          description: Payment session id (`ps_…`).
        order_number:
          type: string
          description: Paymee's order number (`PM-…`).
        merchant_reference:
          type: string
        order_id:
          type: string
          description: Empty when not given.
        amount:
          type: string
          description: Charged amount, decimal string (TND, 3 decimals).
        currency:
          type: string
        original:
          allOf:
          - $ref: '#/components/schemas/OriginalAmount'
          description: What you requested, and the exchange rate applied.
        description:
          type: string
        status:
          enum:
          - CREATED
          - PENDING
          - PROCESSING
          - SUCCEEDED
          - FAILED
          - EXPIRED
          - CANCELED
          type: string
          description: |-
            * `CREATED` - CREATED
            * `PENDING` - PENDING
            * `PROCESSING` - PROCESSING
            * `SUCCEEDED` - SUCCEEDED
            * `FAILED` - FAILED
            * `EXPIRED` - EXPIRED
            * `CANCELED` - CANCELED
          x-spec-enum-id: cb62bfcc2231b12c
        buyer:
          $ref: '#/components/schemas/WebhookBuyer'
        items:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: The line items you sent, as sent.
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Your metadata, values as strings.
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
          description: When the session reached a final status; null while it is open.
        last_failure_code:
          type: string
          description: The last decline's acquirer code. Informational.
        last_failure_message:
          type: string
          description: The last decline's acquirer reason. Informational.
        attempts_count:
          type: integer
        can_retry:
          type: boolean
          description: 'Always false: the session is final.'
      required:
      - amount
      - attempts_count
      - buyer
      - can_retry
      - completed_at
      - currency
      - description
      - id
      - items
      - last_failure_code
      - last_failure_message
      - merchant_reference
      - metadata
      - order_id
      - order_number
      - original
      - status
    WebhookSessionSucceeded:
      type: object
      description: The payment session, as every `session.*` and `checkout.session.expired`
        event carries it.
      properties:
        id:
          type: string
          description: Payment session id (`ps_…`).
        order_number:
          type: string
          description: Paymee's order number (`PM-…`).
        merchant_reference:
          type: string
        order_id:
          type: string
          description: Empty when not given.
        amount:
          type: string
          description: Charged amount, decimal string (TND, 3 decimals).
        currency:
          type: string
        original:
          allOf:
          - $ref: '#/components/schemas/OriginalAmount'
          description: What you requested, and the exchange rate applied.
        description:
          type: string
        status:
          enum:
          - CREATED
          - PENDING
          - PROCESSING
          - SUCCEEDED
          - FAILED
          - EXPIRED
          - CANCELED
          type: string
          description: |-
            * `CREATED` - CREATED
            * `PENDING` - PENDING
            * `PROCESSING` - PROCESSING
            * `SUCCEEDED` - SUCCEEDED
            * `FAILED` - FAILED
            * `EXPIRED` - EXPIRED
            * `CANCELED` - CANCELED
          x-spec-enum-id: cb62bfcc2231b12c
        buyer:
          $ref: '#/components/schemas/WebhookBuyer'
        items:
          type: array
          items:
            type: object
            additionalProperties: {}
          description: The line items you sent, as sent.
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Your metadata, values as strings.
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
          description: When the session reached a final status; null while it is open.
        payment:
          $ref: '#/components/schemas/WebhookPayment'
      required:
      - amount
      - buyer
      - completed_at
      - currency
      - description
      - id
      - items
      - merchant_reference
      - metadata
      - order_id
      - order_number
      - original
      - payment
      - status
  securitySchemes:
    MerchantApiKey:
      type: http
      scheme: bearer
      description: |-
        Authenticate with your **merchant secret API key** in the `Authorization` header:

        ```
        Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        ```

        Use a `sk_live_` key against production and a `sk_test_` key against the sandbox. Keep secret keys server-side; never expose them in browser or mobile clients.
servers:
- url: https://api.paymee.online/api/v1
  description: Production (test & live, by key)
tags:
- name: Sessions
  description: Create and manage payment sessions and their checkouts.
- name: Refunds
  description: Refund succeeded payment sessions.
- name: Webhook deliveries
  description: Inspect the delivery history of the webhooks sent to your endpoint.
    The events themselves are described under Webhooks.
webhooks:
  session.succeeded:
    post:
      description: 'The payment succeeded. The session is `SUCCEEDED` (final): fulfil
        the order.'
      summary: session.succeeded
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SessionSucceededEvent'
        required: true
      responses:
        '200':
          description: Any `2xx` within 10 seconds acknowledges the event; anything
            else is retried.
  session.attempt_failed:
    post:
      description: A payment attempt was declined. While `can_retry` is true the buyer
        can try again on the hosted page; the last allowed decline is followed by
        `session.failed`.
      summary: session.attempt_failed
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SessionAttemptFailedEvent'
        required: true
      responses:
        '200':
          description: Any `2xx` within 10 seconds acknowledges the event; anything
            else is retried.
  session.failed:
    post:
      description: 'Every allowed attempt was declined. The session is `FAILED` (final):
        to try again, create a new session with a new `merchant_reference`.'
      summary: session.failed
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SessionFailedEvent'
        required: true
      responses:
        '200':
          description: Any `2xx` within 10 seconds acknowledges the event; anything
            else is retried.
  session.expired:
    post:
      description: The session reached the end of its lifetime (about 24 hours) unpaid.
        It is `EXPIRED` (final).
      summary: session.expired
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SessionExpiredEvent'
        required: true
      responses:
        '200':
          description: Any `2xx` within 10 seconds acknowledges the event; anything
            else is retried.
  checkout.session.expired:
    post:
      description: 'A hosted-page link expired (about 30 minutes). The payment session
        itself stays payable: no action needed.'
      summary: checkout.session.expired
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckoutSessionExpiredEvent'
        required: true
      responses:
        '200':
          description: Any `2xx` within 10 seconds acknowledges the event; anything
            else is retried.
  refund.succeeded:
    post:
      description: A refund returned the money to the buyer, whichever channel issued
        it (API, dashboard, or a plugin).
      summary: refund.succeeded
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefundSucceededEvent'
        required: true
      responses:
        '200':
          description: Any `2xx` within 10 seconds acknowledges the event; anything
            else is retried.
  refund.failed:
    post:
      description: An accepted refund ended without the money being returned.
      summary: refund.failed
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefundFailedEvent'
        required: true
      responses:
        '200':
          description: Any `2xx` within 10 seconds acknowledges the event; anything
            else is retried.
