openapi: 3.1.0

info:
  title: Pesepay API
  version: "1.0.0"
  summary: Accept EcoCash, InnBucks, Omari, PayGo, Zimswitch and Visa/Mastercard payments in USD and Zimbabwe dollars.
  description: |
    The Pesepay payments API, covering the endpoints a merchant integration
    uses: creating a transaction, submitting a seamless payment, checking a
    transaction's status, and reading the currencies and payment methods
    available on the platform.

    ## How the request and response bodies are modelled here

    `POST /v1/payments/initiate`, `POST /v2/payments/make-payment` and
    `GET /v1/payments/check-payment` **do not send or receive plain JSON on the
    wire.** The actual body in both directions is a one-field envelope:

    ```json
    { "payload": "<base64 AES-256-CBC ciphertext>" }
    ```

    The `payload` is the JSON body — the schema shown for each operation below —
    encrypted with **AES-256-CBC**, base64-encoded. The key is your
    32-character application **encryption key**; the IV is the **first 16
    characters of that same key**. You encrypt your request body into the
    envelope before sending, and decrypt the response envelope before reading a
    field.

    **This spec models the decrypted JSON as the request and response body**,
    because that is what is useful for generating models, field tables and SDK
    types — the envelope is a uniform transport wrapper an SDK applies once.
    Operations that use the envelope are marked `x-pesepay-transport:
    aes-256-cbc-envelope`. The envelope itself is
    [`EncryptedEnvelope`](#/components/schemas/EncryptedEnvelope). Full
    walkthrough: [Encryption Guide](https://developers.pesepay.com/security/encryption/).

    ## Other things to know before generating code from this spec

    - **There are two different keys.** The *integration key* goes in the
      `authorization` header on every authenticated call. The *encryption key*
      is used only for the payload cipher above. They are not interchangeable.

    - **Card payments are redirect-only.** Visa, Mastercard and Zimswitch have
      no seamless integration and cannot be used with
      `POST /v2/payments/make-payment`. Send a card payment through
      `POST /v1/payments/initiate` and redirect the customer to the returned
      `redirectUrl`. Only mobile-money methods (EcoCash, InnBucks, Omari,
      PayGo) work in the seamless flow.

    - **Zimbabwe dollars are addressed by two distinct currency codes**, which
      are not aliases: `ZiG` holds EcoCash and PayGo, `ZWG` holds Zimswitch and
      Omari. `GET /v1/currencies/active` returns only `ZiG`, so a currency
      list built from that endpoint silently omits the `ZWG` methods.

    - **`GET /v1/currencies/active` and `GET /v1/payment-methods/for-currency`
      return plain JSON** and take no `authorization` header. Every other
      endpoint here is authenticated and uses the encrypted envelope.

    - **Result callbacks are unsigned, unencrypted and never retried.** Pesepay
      POSTs [`PaymentTransactionResult`](#/components/schemas/PaymentTransactionResult)
      as plain JSON to your `resultUrl` — see the `resultCallback` entry under
      `webhooks`. Confirm every payment by calling
      `GET /v1/payments/check-payment`; never trust the callback body on its
      own.

    - **The sandbox is USD-only** and exposes just EcoCash (`PZW211`), Visa
      (`PZW204`) and Mastercard (`PZW205`), with different amount limits from
      production. InnBucks, Omari, Zimswitch, PayGo and any Zimbabwe dollar
      checkout cannot be tested there.

    - **The published SDKs target production only.** They hardcode the
      production base URL with no sandbox override, so sandbox work has to be
      done over raw HTTP.

    - **Pesepay does not currently enforce rate limits** — there are no
      per-second or per-day quotas and no `429` responses. Poll
      `check-payment` on a sensible interval anyway and stop at a terminal
      status.
  contact:
    name: Pesepay developer support
    url: https://developers.pesepay.com
  license:
    name: Proprietary — © Pesepay
    url: https://developers.pesepay.com

externalDocs:
  description: Pesepay developer documentation
  url: https://developers.pesepay.com

servers:
  - url: https://api.pesepay.com/api/payments-engine
    description: Production
  - url: https://api.test.sandbox.pesepay.com/payments-engine
    description: Sandbox — USD only, EcoCash / Visa / Mastercard, limits differ from production

security:
  - integrationKey: []

tags:
  - name: Payments
    description: Create, submit and check transactions. All three endpoints use the encrypted envelope.
  - name: Metadata
    description: Read the currencies and payment methods available on the platform. Plain JSON, no authentication.

paths:
  /v1/payments/initiate:
    post:
      operationId: initiateTransaction
      tags: [Payments]
      summary: Initiate a transaction
      x-pesepay-transport: aes-256-cbc-envelope
      description: |
        Creates a transaction and returns a `redirectUrl` where the customer
        completes payment on the Pesepay-hosted page. This is the first call in
        the redirect (checkout) flow, and the only way to accept card payments.

        On the wire the request body is
        `{ "payload": "<encrypted request JSON>" }` and the `200` body is
        `{ "payload": "<encrypted response JSON>" }`. The schemas below are the
        **decrypted** JSON on each side.

        The response carries **only** `referenceNumber`, `pollUrl` and
        `redirectUrl` — no status or amount detail. Store `referenceNumber`,
        send the customer to `redirectUrl`, then call
        `GET /v1/payments/check-payment` for the outcome.
      requestBody:
        required: true
        description: The decrypted request JSON. Encrypt it into `EncryptedEnvelope.payload` before sending.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InitiateTransactionRequest"
      responses:
        "200":
          description: |
            Transaction created. On the wire this is
            `EncryptedEnvelope`; the schema shown is the decrypted `payload`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InitiateTransactionResult"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/ServerError"

  /v2/payments/make-payment:
    post:
      operationId: makeSeamlessPayment
      tags: [Payments]
      summary: Make a seamless payment
      x-pesepay-transport: aes-256-cbc-envelope
      description: |
        Creates a transaction and submits it directly to a mobile-money
        payment method in one call, without redirecting to a hosted page. Your
        UI collects the method's required fields (see
        `GET /v1/payment-methods/for-currency`) and sends them in
        `paymentMethodRequiredFields`.

        Available for **mobile-money methods only** — EcoCash, InnBucks, Omari,
        PayGo. Card methods (Visa, Mastercard, Zimswitch) reject this call; use
        `POST /v1/payments/initiate` for those.

        This is `v2`. The `v1` seamless path is deprecated in production and
        does not exist in the sandbox.

        On the wire, request and `200` bodies are `EncryptedEnvelope`; the
        schemas below are the decrypted JSON. The response is
        `PaymentTransactionResult`, the same object returned by
        `check-payment` and posted to your `resultUrl`. A `200` here does
        **not** mean you were paid — the status is usually non-terminal while
        the customer approves the payment on their handset. Wait for the
        callback or poll `pollUrl` until the status is terminal, and treat only
        `SUCCESS` as paid.
      requestBody:
        required: true
        description: The decrypted request JSON. Encrypt it into `EncryptedEnvelope.payload` before sending.
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SeamlessPaymentRequest"
      responses:
        "200":
          description: |
            Transaction created and submitted. On the wire this is
            `EncryptedEnvelope`; the schema shown is the decrypted `payload`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentTransactionResult"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/payments/check-payment:
    get:
      operationId: checkPayment
      tags: [Payments]
      summary: Check payment status
      x-pesepay-transport: aes-256-cbc-envelope
      description: |
        Returns the current state of a transaction by reference number. Use it
        as the source of truth for whether a payment succeeded — the result
        callback proves nothing on its own, and failed callback deliveries are
        never retried.

        The `200` body on the wire is `EncryptedEnvelope`; the schema shown is
        the decrypted `payload`,
        [`PaymentTransactionResult`](#/components/schemas/PaymentTransactionResult).
        Poll on a backoff and stop once `transactionStatus` is terminal.
      parameters:
        - name: referenceNumber
          in: query
          required: true
          description: The `referenceNumber` returned when the transaction was created.
          schema:
            type: string
          example: "20260901103214123-A1B2C3D4"
      responses:
        "200":
          description: |
            On the wire this is `EncryptedEnvelope`; the schema shown is the
            decrypted `payload`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PaymentTransactionResult"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/currencies/active:
    get:
      operationId: getActiveCurrencies
      tags: [Metadata]
      summary: Get active currencies
      description: |
        Returns the currencies currently active on the platform, as plain
        JSON. No `authorization` header.

        Today this returns `USD` and `ZiG` only. `ZWG` is also chargeable — it
        is the currency code for Zimswitch and Omari in Zimbabwe dollars — but
        it does not appear here. A currency selector built purely off this
        response silently drops the `ZWG` methods.
      security: []
      responses:
        "200":
          description: An array of currencies.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Currency"
              example:
                - id: 1
                  name: "United States Dollar"
                  description: "US Dollar"
                  code: "USD"
                  defaultCurrency: true
                  rateToDefault: 1
                  active: true
                - id: 2
                  name: "Zimbabwe Gold"
                  description: "Zimbabwe Gold"
                  code: "ZiG"
                  defaultCurrency: false
                  rateToDefault: 26.5
                  active: true
        "500":
          $ref: "#/components/responses/ServerError"

  /v1/payment-methods/for-currency:
    get:
      operationId: getPaymentMethodsForCurrency
      tags: [Metadata]
      summary: Get payment methods for a currency
      description: |
        Returns the payment methods available for a currency, as plain JSON.
        No `authorization` header. This is the live source of truth for
        method codes, per-currency amount limits and required fields — build a
        dynamic method picker and seamless-flow form from it instead of
        hardcoding.

        Query with the currency code for the method you intend to charge:
        `USD`, `ZiG` (EcoCash, PayGo) or `ZWG` (Zimswitch, Omari). `ZWL`
        returns an empty array.
      security: []
      parameters:
        - name: currencyCode
          in: query
          required: true
          description: The currency to list methods for, e.g. `USD`, `ZiG` or `ZWG`.
          schema:
            type: string
          example: "USD"
      responses:
        "200":
          description: An array of payment methods available for the currency.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/PaymentMethod"
              example:
                - id: 11
                  name: "EcoCash USD"
                  description: "Pay with EcoCash"
                  code: "PZW211"
                  maximumAmount: 500
                  minimumAmount: 1
                  redirectRequired: false
                  redirectURL: null
                  active: true
                  currencies: ["USD"]
                  processingPaymentMessage: "You will receive a prompt on your phone. Enter your EcoCash PIN to approve the payment."
                  requiredFields:
                    - name: "customerPhoneNumber"
                      displayName: "Phone Number"
                      fieldType: "TEXT"
                      optional: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "500":
          $ref: "#/components/responses/ServerError"

webhooks:
  resultCallback:
    post:
      operationId: resultCallback
      summary: Transaction result callback
      description: |
        When a transaction reaches a terminal status, Pesepay POSTs the result
        to the `resultUrl` you set when creating it. **Your** server is the
        endpoint here.

        Unlike every other Pesepay response, this body is **plain JSON, not
        encrypted** — do not run it through decryption. It is **not signed**;
        the only header is `Authorization`, carrying your own integration key.
        Failed deliveries are **not retried**. Acknowledge with `2xx` quickly,
        then confirm the payment with `GET /v1/payments/check-payment` before
        acting on it, and run a reconciliation job for callbacks that never
        arrive.

        Fires once per transaction, only for terminal statuses. Non-terminal
        statuses (`PENDING`, `PROCESSING`, `PARTIALLY_PAID`) produce no
        callback.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PaymentTransactionResult"
      responses:
        "200":
          description: Any `2xx` acknowledges receipt. Pesepay does not read the body.

components:
  securitySchemes:
    integrationKey:
      type: apiKey
      in: header
      name: authorization
      description: |
        Your application's **integration key**, sent as the raw header value
        (no `Bearer` prefix). Distinct from the encryption key used for the
        payload cipher. Find it under
        [Onboarding](https://developers.pesepay.com/getting-started/onboarding/).
        Never send it from browser or mobile-app code.

  responses:
    BadRequest:
      description: |
        The request was rejected — a missing or invalid field, a failed
        validator, or a payload that decrypted to something that is not valid
        JSON. Multiple field errors are joined into one `message` with `;`.
        These are integration bugs; do not retry unchanged.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorMessage"
          examples:
            missingField:
              value:
                timestamp: "2026-09-01T10:32:14.000+00:00"
                message: "Currency code should be provided"
                description: null
                status: "400"
            validation:
              value:
                timestamp: "2026-09-01T10:32:14.000+00:00"
                message: "Invalid Visa Card Number length, its either 13, 16, 19 numbers.; Invalid Credit card expiry date provided"
                description: null
                status: "400"
    Forbidden:
      description: |
        The integration key was rejected (wrong environment, not active, or
        disabled), or no payment method supports the requested currency.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorMessage"
          example:
            timestamp: "2026-09-01T10:32:14.000+00:00"
            message: "Integration key for the application is not valid (Not active or disabled)"
            description: null
            status: "403"
    NotFound:
      description: No transaction matches the reference number.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorMessage"
          example:
            timestamp: "2026-09-01T10:32:14.000+00:00"
            message: "Transaction not found"
            description: null
            status: "404"
    ServerError:
      description: |
        A server-side failure, including a payload that could not be decrypted
        (`Failed to decrypt your data` — check your encryption key and the IV
        rule) and downstream provider errors.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorMessage"
          example:
            timestamp: "2026-09-01T10:32:14.000+00:00"
            message: "Failed to decrypt your data"
            description: null
            status: "500"

  schemas:
    EncryptedEnvelope:
      type: object
      description: |
        The literal wire body for every authenticated endpoint, in both
        directions. `payload` is a base64 string: the operation's JSON body
        encrypted with AES-256-CBC using your 32-character encryption key, with
        the IV taken as the first 16 characters of that key. The operation
        schemas in this spec show the **decrypted** JSON, not this wrapper.
      required: [payload]
      properties:
        payload:
          type: string
          description: Base64-encoded AES-256-CBC ciphertext of the JSON body.
          examples:
            - "u4Jr0nP2b1c9QhF7kLm3wXyZpR8sT2vD1gH6jK0lNqA=="

    ErrorMessage:
      type: object
      description: |
        The body of every error response. Pesepay has no error-code taxonomy —
        branch on the HTTP status, log `message`, and do not pattern-match on
        message text (much of it is passed through verbatim from payment
        providers). Error bodies are plain JSON — never run them through
        decryption.
      required: [timestamp, message, status]
      properties:
        timestamp:
          type: string
          format: date-time
          description: When the error occurred.
        message:
          type: string
          description: What went wrong. Multiple field errors are joined with `;`.
        description:
          type: [string, "null"]
          description: Extra detail. Usually `null`.
        status:
          type: string
          description: The HTTP status code, as a string.
          examples: ["400"]

    AmountDetailsInput:
      type: object
      description: The amount to charge. The only two fields you send; the rest of `AmountDetails` is computed by Pesepay.
      required: [amount, currencyCode]
      properties:
        amount:
          type: number
          description: The amount to charge, in `currencyCode`. Must be within the method's per-currency limits.
          examples: [10.00]
        currencyCode:
          type: string
          description: "`USD`, `ZiG` or `ZWG` — the code for the method you are charging."
          examples: ["USD"]

    AmountDetails:
      type: object
      description: Full amount breakdown returned on a transaction result.
      properties:
        amount:
          type: number
          description: The amount charged, in `currencyCode`.
        currencyCode:
          type: string
        defaultCurrencyAmount:
          type: number
          description: "`amount` converted to the platform's default currency."
        defaultCurrencyCode:
          type: string
        transactionServiceFee:
          type: number
          description: The Pesepay service fee applied to the transaction.
        customerPayableAmount:
          type: number
          description: What the customer actually pays.
        totalTransactionAmount:
          type: number
          description: Total amount for the transaction.
        merchantAmount:
          type: number
          description: What you receive.

    CustomerDetails:
      type: object
      description: Optional customer contact details carried on the transaction.
      properties:
        email:
          type: string
          format: email
        phoneNumber:
          type: string
        name:
          type: string

    TransactionMetadata:
      type: object
      description: Free-form string key/value pairs echoed back on the transaction and its result.
      additionalProperties:
        type: string
      examples:
        - orderId: "1042"

    InitiateTransactionRequest:
      type: object
      description: |
        Decrypted request body for `POST /v1/payments/initiate`. Encrypt the
        whole object into `EncryptedEnvelope.payload` before sending. Fields
        the server sets (`applicationId`, `applicationName`, `applicationCode`,
        `chargeType`, `transactionType`) must not be sent.
      required: [amountDetails, reasonForPayment, resultUrl, returnUrl]
      properties:
        amountDetails:
          $ref: "#/components/schemas/AmountDetailsInput"
        reasonForPayment:
          type: string
          description: A short summary of the transaction. Shown to the customer.
          examples: ["Order #1042 — running shoes"]
        resultUrl:
          type: string
          format: uri
          description: Where Pesepay POSTs the final transaction result. An empty value is stored as `"NONE"`.
          examples: ["https://example.com/payments/result"]
        returnUrl:
          type: string
          format: uri
          description: Where the customer's browser is sent after completing or cancelling. An empty value is stored as `"NONE"`.
          examples: ["https://example.com/payments/return"]
        merchantReference:
          type: string
          description: Your own order/invoice reference, echoed back on the transaction.
          examples: ["ORDER-1042"]
        paymentMethodCode:
          type: string
          description: Optional. Pre-selects a payment method so the hosted page opens straight into it.
          examples: ["PZW204"]
        paymentMetadata:
          $ref: "#/components/schemas/TransactionMetadata"

    InitiateTransactionResult:
      type: object
      description: |
        Decrypted `POST /v1/payments/initiate` response. These three fields are
        all it carries — there is no status or amount detail here. Call
        `GET /v1/payments/check-payment` for those.
      properties:
        referenceNumber:
          type: string
          description: Pesepay's reference for the transaction. Store it — it is your primary tracking key.
          examples: ["20260901103214123-A1B2C3D4"]
        pollUrl:
          type: string
          format: uri
          description: A ready-made `check-payment` URL for this transaction.
        redirectUrl:
          type: string
          format: uri
          description: Send the customer here to complete payment.

    SeamlessPaymentRequest:
      type: object
      description: |
        Decrypted request body for `POST /v2/payments/make-payment`. Encrypt
        the whole object into `EncryptedEnvelope.payload` before sending.
      required: [amountDetails, reasonForPayment, resultUrl, paymentMethodCode]
      properties:
        amountDetails:
          $ref: "#/components/schemas/AmountDetailsInput"
        reasonForPayment:
          type: string
          examples: ["Order #1042 — running shoes"]
        resultUrl:
          type: string
          format: uri
          description: Where Pesepay POSTs the final transaction result.
        returnUrl:
          type: string
          format: uri
          description: Optional. Defaults to `resultUrl` when omitted or blank.
        paymentMethodCode:
          type: string
          description: |
            The method to charge. Mobile-money only — EcoCash (`PZW211` /
            `PZW201`), InnBucks (`PZW212`), Omari (`PZW216` / `PZW217`), PayGo
            (`PZW210`). Card codes are rejected here.
          examples: ["PZW211"]
        merchantReference:
          type: string
          examples: ["ORDER-1042"]
        customer:
          $ref: "#/components/schemas/CustomerDetails"
        paymentMethodRequiredFields:
          type: object
          description: |
            The method's required fields, as string key/value pairs. Take the
            keys from that method's `requiredFields` in
            `GET /v1/payment-methods/for-currency`. EcoCash and Omari need
            `customerPhoneNumber`; InnBucks and PayGo take an empty object.
          additionalProperties:
            type: string
          examples:
            - customerPhoneNumber: "0771111111"
        paymentMetadata:
          $ref: "#/components/schemas/TransactionMetadata"

    PaymentTransactionResult:
      type: object
      description: |
        Decrypted response from `POST /v2/payments/make-payment` and
        `GET /v1/payments/check-payment`, and the exact body Pesepay POSTs to
        your `resultUrl` (there, as plain JSON, unencrypted). There is no
        `redirectUrl` field on this object.
      properties:
        referenceNumber:
          type: string
          description: Pesepay's reference for the transaction. Match it to your order.
          examples: ["20260901103214123-A1B2C3D4"]
        dateOfTransaction:
          type: string
          format: date-time
          description: When the transaction was created.
        applicationId:
          type: integer
          format: int64
          description: The Pesepay application the transaction belongs to.
        applicationName:
          type: string
        amountDetails:
          $ref: "#/components/schemas/AmountDetails"
        reasonForPayment:
          type: string
        transactionStatus:
          $ref: "#/components/schemas/TransactionStatus"
        transactionStatusCode:
          type: integer
          description: Numeric equivalent of `transactionStatus`. `307` is shared by `CLOSED` and `CLOSED_PERIOD_ELAPSED`.
          examples: [304]
        transactionStatusDescription:
          type: string
          examples: ["Transaction was successfully completed"]
        resultUrl:
          type: string
          format: uri
        returnUrl:
          type: string
          format: uri
        pollUrl:
          type: string
          format: uri
        transactionMetadata:
          $ref: "#/components/schemas/TransactionMetadata"
        splits:
          type: array
          description: |
            Reserved for split-payment detail. **Always empty** on this
            response and on the callback — per-leg outcomes of a split EcoCash
            payment are never exposed to merchants.
          items:
            type: object

    TransactionStatus:
      type: string
      description: |
        Where a payment stands. **Terminal** statuses are final and trigger a
        result callback; stop polling when you see one. `SUCCESS` is the only
        status that means you were paid. `PARTIALLY_PAID` is **not** terminal.
        Numeric codes and terminal flags are in `x-pesepay-status-codes`.
      enum:
        - INITIATED
        - PROCESSING
        - PENDING
        - PARTIALLY_PAID
        - SUCCESS
        - FAILED
        - TERMINATED
        - TIME_OUT
        - CLOSED
        - CLOSED_PERIOD_ELAPSED
        - INSUFFICIENT_FUNDS
        - CANCELLED
        - ERROR
        - DECLINED
        - AUTHORIZATION_FAILED
        - SERVICE_UNAVAILABLE
        - REVERSED
      x-pesepay-status-codes:
        INITIATED: { code: 301, terminal: false }
        PROCESSING: { code: 302, terminal: false }
        PENDING: { code: 303, terminal: false }
        PARTIALLY_PAID: { code: 315, terminal: false }
        SUCCESS: { code: 304, terminal: true }
        FAILED: { code: 300, terminal: true }
        TERMINATED: { code: 305, terminal: true }
        TIME_OUT: { code: 306, terminal: true }
        CLOSED: { code: 307, terminal: true }
        CLOSED_PERIOD_ELAPSED: { code: 307, terminal: true }
        INSUFFICIENT_FUNDS: { code: 308, terminal: true }
        CANCELLED: { code: 309, terminal: true }
        ERROR: { code: 310, terminal: true }
        DECLINED: { code: 311, terminal: true }
        AUTHORIZATION_FAILED: { code: 312, terminal: true }
        SERVICE_UNAVAILABLE: { code: 313, terminal: true }
        REVERSED: { code: 314, terminal: true }

    Currency:
      type: object
      description: A currency returned by `GET /v1/currencies/active`.
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
          examples: ["United States Dollar"]
        description:
          type: string
        code:
          type: string
          description: The code you send as `currencyCode`.
          examples: ["USD"]
        defaultCurrency:
          type: boolean
          description: Whether this is the platform's default currency.
        rateToDefault:
          type: number
          description: Exchange rate to the default currency.
        active:
          type: boolean

    PaymentMethod:
      type: object
      description: A payment method returned by `GET /v1/payment-methods/for-currency`.
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
          examples: ["EcoCash USD"]
        description:
          type: string
        code:
          type: string
          description: The value to send as `paymentMethodCode`.
          examples: ["PZW211"]
        maximumAmount:
          type: number
          description: Maximum transactable amount for this method in the queried currency.
        minimumAmount:
          type: number
          description: Minimum transactable amount for this method in the queried currency.
        redirectRequired:
          type: boolean
          description: "`true` for card methods — they cannot be used with the seamless endpoint."
        redirectURL:
          type: [string, "null"]
        active:
          type: boolean
        currencies:
          type: array
          items:
            type: string
        processingPaymentMessage:
          type: string
          description: Provider-supplied copy to show the customer while the payment processes.
        requiredFields:
          type: array
          description: The fields to collect and send in `paymentMethodRequiredFields`.
          items:
            $ref: "#/components/schemas/RequiredField"

    RequiredField:
      type: object
      description: One field a payment method needs in the seamless flow.
      properties:
        name:
          type: string
          description: The key to use in `paymentMethodRequiredFields`.
          examples: ["customerPhoneNumber"]
        displayName:
          type: string
          description: A human label for your form.
          examples: ["Phone Number"]
        fieldType:
          type: string
          enum: [TEXT, NUMBER, FILE, DATE]
        optional:
          type: boolean
