> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moduluslabs.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Payment

> Initiate a payment to a specific terminal with long-polling up to 90 seconds

<Note>
  This endpoint requires HMAC-SHA256 authentication. See [Authentication](../../docs/terminal-gateway/authentication) for signature computation details.
</Note>

## Long-Polling Behavior

This endpoint uses long-polling and waits up to **90 seconds** for the terminal to respond. Plan for this timeout in your HTTP client configuration.

## Terminal ID Resolution

The `terminalId` parameter is resolved in the following order:

1. **Device ID (Recommended)** - First checks if the ID matches a registered `deviceId`
2. **Connection ID (Fallback)** - If no device ID match, treats it as a `connectionId`

<Tip>
  Always use `deviceId` when available. It provides a stable reference that doesn't change when terminals reconnect.
</Tip>

## Handling Timeouts

If you receive a `504 Gateway Timeout` response:

1. **Do not** retry the payment immediately
2. Use [Get Transaction](./get-transaction) to check if the payment completed
3. Only retry if the transaction status is `FAILED` or `CANCELLED`

<Warning>
  The payment may have completed on the terminal after the HTTP timeout. Always verify transaction status before retrying to avoid duplicate charges.
</Warning>


## OpenAPI

````yaml api-reference/terminal-gateway/openapi.json POST /v1/terminals/{terminalId}/payments
openapi: 3.1.0
info:
  title: Terminal Gateway HTTP API
  version: 1.0.0
  description: >-
    HTTP API for POS-to-terminal communication. Enables payment processing
    through connected terminals.
  contact:
    name: Modulus Labs Support
    email: support@moduluslabs.io
servers:
  - url: https://your-endpoint.moduluslabs.io
    description: >-
      Your provisioned Terminal Gateway API endpoint. Contact
      support@moduluslabs.io to obtain your URL and credentials.
security:
  - hmacAuth: []
tags:
  - name: Terminals
    description: Terminal discovery and management
  - name: Payments
    description: Payment processing
  - name: Transactions
    description: Transaction status and reconciliation
  - name: Webhooks
    description: Webhook endpoint management for asynchronous payment notifications
paths:
  /v1/terminals/{terminalId}/payments:
    post:
      tags:
        - Payments
      summary: Create Payment
      description: >-
        Initiate a payment to a specific terminal. By default, this endpoint
        uses long-polling and waits up to 90 seconds for the terminal to
        respond. Set webhookMode to true for immediate response with async
        notification. The terminalId can be either a deviceId (recommended) or
        connectionId.
      operationId: createPayment
      parameters:
        - name: terminalId
          in: path
          required: true
          description: >-
            The terminal identifier. Can be either a deviceId (recommended) or
            connectionId (legacy fallback).
          schema:
            type: string
          example: TERM-001
      requestBody:
        required: true
        description: Payment request details
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentRequest'
            examples:
              standard:
                summary: Standard payment (long-polling)
                value:
                  transactionId: TXN-20240115-001
                  amount: '99.99'
                  currency: USD
                  paymentMethod: CARD
                  products:
                    - id: PROD-001
                      name: Widget
                      price: '99.99'
                      quantity: 1
                  customerInfo:
                    customerId: CUST-123
                    email: customer@example.com
                  metadata:
                    orderId: ORD-12345
              webhookMode:
                summary: Webhook mode (async notification)
                value:
                  transactionId: TXN-20240115-002
                  amount: '150.00'
                  currency: USD
                  paymentMethod: CARD
                  webhookMode: true
                  metadata:
                    orderId: ORD-12346
      responses:
        '200':
          description: Payment completed successfully (standard mode)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentResult'
              example:
                transactionId: TXN-20240115-001
                status: SUCCESS
                paymentResponse:
                  transactionId: TXN-20240115-001
                  status: SUCCESS
                  amount: '99.99'
                  currency: USD
                  paymentMethod: CARD
                  authorizationCode: AUTH123456
                  receiptData: ...
                  timestamp: '2024-01-15T10:37:30.000Z'
                timestamp: '2024-01-15T10:37:30.000Z'
        '202':
          description: >-
            Payment accepted for processing. Returned when webhookMode is true,
            or when terminal disconnects during processing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookAcceptedResponse'
              examples:
                webhookMode:
                  summary: Webhook mode accepted
                  value:
                    transactionId: TXN-20240115-002
                    status: ACCEPTED
                    message: >-
                      Payment sent to terminal. Result will be delivered via
                      webhook.
                    timestamp: '2024-01-15T10:36:00.000Z'
                terminalDisconnected:
                  summary: Terminal disconnected
                  value:
                    transactionId: TXN-20240115-001
                    status: PENDING
                    error:
                      code: TERMINAL_CONNECTION_ERROR
                      message: Terminal disconnected during payment processing
                    timestamp: '2024-01-15T10:37:30.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '502':
          $ref: '#/components/responses/BadGateway'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
components:
  schemas:
    PaymentRequest:
      type: object
      required:
        - amount
        - currency
        - paymentMethod
      properties:
        transactionId:
          type: string
          description: Unique transaction identifier. Auto-generated if not provided.
        amount:
          oneOf:
            - type: string
            - type: number
          description: Payment amount
        currency:
          type: string
          description: ISO 4217 currency code (e.g., USD, EUR, PHP)
        paymentMethod:
          type: string
          enum:
            - CARD
            - CASH
            - MOBILE
            - OTHER
          description: Payment method
        webhookMode:
          type: boolean
          default: false
          description: >-
            When true, disables 90-second long-polling and returns 202 Accepted
            immediately. Payment result will be delivered via webhook. Requires
            at least one webhook endpoint configured.
        products:
          type: array
          description: Array of products in the transaction
          items:
            $ref: '#/components/schemas/Product'
        customerInfo:
          $ref: '#/components/schemas/CustomerInfo'
        metadata:
          type: object
          description: Custom key-value pairs for your use
          additionalProperties: true
    PaymentResult:
      type: object
      required:
        - transactionId
        - status
        - timestamp
      properties:
        transactionId:
          type: string
          description: Transaction identifier
        status:
          type: string
          enum:
            - SUCCESS
            - FAILED
            - PENDING
            - CANCELLED
          description: Payment result status
        paymentResponse:
          $ref: '#/components/schemas/PaymentResponse'
        timestamp:
          type: string
          format: date-time
          description: ISO 8601 timestamp
    WebhookAcceptedResponse:
      type: object
      required:
        - transactionId
        - status
        - timestamp
      properties:
        transactionId:
          type: string
          description: Transaction identifier
        status:
          type: string
          enum:
            - ACCEPTED
            - PENDING
          description: Request acceptance status
        message:
          type: string
          description: Human-readable status message
        error:
          type: object
          properties:
            code:
              type: string
              description: Error code
            message:
              type: string
              description: Error message
        timestamp:
          type: string
          format: date-time
          description: ISO 8601 timestamp
    Product:
      type: object
      required:
        - id
        - name
        - price
        - quantity
      properties:
        id:
          type: string
          description: Product identifier
        name:
          type: string
          description: Product name
        price:
          oneOf:
            - type: string
            - type: number
          description: Unit price
        quantity:
          type: number
          description: Quantity
        category:
          type: string
          description: Product category
        sku:
          type: string
          description: Stock keeping unit
        brand:
          type: string
          description: Brand name
        tax:
          type: number
          description: Tax amount
        discount:
          type: number
          description: Discount amount
    CustomerInfo:
      type: object
      properties:
        customerId:
          type: string
          description: Customer identifier
        email:
          type: string
          format: email
          description: Customer email address
        phone:
          type: string
          description: Customer phone number
    PaymentResponse:
      type: object
      required:
        - transactionId
        - status
        - amount
        - currency
        - paymentMethod
        - timestamp
      properties:
        transactionId:
          type: string
          description: Transaction identifier
        status:
          type: string
          enum:
            - SUCCESS
            - FAILED
            - PENDING
            - CANCELLED
          description: Payment status
        amount:
          oneOf:
            - type: string
            - type: number
          description: Payment amount
        currency:
          type: string
          description: Currency code
        paymentMethod:
          type: string
          description: Payment method used
        authorizationCode:
          type: string
          description: Authorization code from payment processor (success only)
        errorCode:
          type: string
          description: Error code if status is FAILED
        errorMessage:
          type: string
          description: Human-readable error message if status is FAILED
        receiptData:
          type: string
          description: Opaque receipt data from terminal
        timestamp:
          type: string
          format: date-time
          description: ISO 8601 timestamp of when the payment was processed
        metadata:
          type: object
          description: Custom metadata returned from the original request
          additionalProperties: true
    Error:
      type: object
      required:
        - error
        - timestamp
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Error code
            message:
              type: string
              description: Human-readable error message
            details:
              type: object
              description: Additional error details
              additionalProperties: true
        timestamp:
          type: string
          format: date-time
          description: ISO 8601 timestamp
        requestId:
          type: string
          description: Request identifier for debugging
        transactionId:
          type: string
          description: Transaction ID if applicable
  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            invalidRequest:
              summary: Invalid request body
              value:
                error:
                  code: INVALID_REQUEST
                  message: Request body is malformed or missing required fields
                timestamp: '2024-01-15T10:40:00.000Z'
            invalidAmount:
              summary: Invalid amount
              value:
                error:
                  code: INVALID_AMOUNT
                  message: Amount is invalid or doesn't match products total
                timestamp: '2024-01-15T10:40:00.000Z'
            noWebhookEndpoints:
              summary: No webhook endpoints configured
              value:
                error:
                  code: NO_WEBHOOK_ENDPOINTS
                  message: >-
                    webhookMode requires at least one webhook endpoint to be
                    configured
                timestamp: '2024-01-15T10:40:00.000Z'
    Unauthorized:
      description: Authentication failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            unauthorized:
              summary: Invalid API key
              value:
                error:
                  code: UNAUTHORIZED
                  message: Missing or invalid API key
                timestamp: '2024-01-15T10:40:00.000Z'
            invalidSignature:
              summary: Invalid signature
              value:
                error:
                  code: INVALID_SIGNATURE
                  message: HMAC signature verification failed
                timestamp: '2024-01-15T10:40:00.000Z'
            timestampExpired:
              summary: Timestamp expired
              value:
                error:
                  code: TIMESTAMP_EXPIRED
                  message: Request timestamp outside 5-minute window
                timestamp: '2024-01-15T10:40:00.000Z'
    Forbidden:
      description: Access denied
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: GROUP_MISMATCH
              message: Resource belongs to a different group
            timestamp: '2024-01-15T10:40:00.000Z'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            terminalNotFound:
              summary: Terminal not found
              value:
                error:
                  code: TERMINAL_NOT_FOUND
                  message: Terminal not found or offline
                timestamp: '2024-01-15T10:40:00.000Z'
            transactionNotFound:
              summary: Transaction not found
              value:
                error:
                  code: TRANSACTION_NOT_FOUND
                  message: Transaction not found
                timestamp: '2024-01-15T10:40:00.000Z'
    Conflict:
      description: Conflict with current state
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: PAYMENT_IN_PROGRESS
              message: Another payment is being processed on this terminal
            timestamp: '2024-01-15T10:40:00.000Z'
    BadGateway:
      description: Terminal communication error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: TERMINAL_CONNECTION_ERROR
              message: Failed to communicate with terminal
            timestamp: '2024-01-15T10:40:00.000Z'
    ServiceUnavailable:
      description: Terminal offline
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: TERMINAL_OFFLINE
              message: Terminal is offline or unresponsive
            timestamp: '2024-01-15T10:40:00.000Z'
    GatewayTimeout:
      description: Terminal did not respond in time
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: TIMEOUT
              message: Terminal did not respond within 90 seconds
              details:
                note: >-
                  Transaction may still be processed. Check status via GET
                  /v1/transactions/{id}
            transactionId: TXN-20240115-001
            timestamp: '2024-01-15T10:39:00.000Z'
  securitySchemes:
    hmacAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        HMAC-SHA256 authentication. Requires three headers: x-api-key (your API
        key), x-timestamp (ISO 8601 timestamp), and x-signature (Base64-encoded
        HMAC-SHA256 signature). See Authentication documentation for signature
        computation.

````