Skip to content

Cards API Specification

Owner Classification Review Date Status
CDO Office Internal April 2027 Active
openapi: 3.1.0
info:
  title: Simpaisa Cards API
  version: 3.0.0
  description: |
    Simpaisa Cards API enables merchants to accept card payments (Visa,
    Mastercard, UnionPay) across Pakistan, Bangladesh, Nepal, and
    Iraq. The API supports authorisation, capture, void, refund and 3-D Secure
    challenge flows.

    Processing 270M+ transactions and over $1B in volume, the cards service
    provides PCI DSS-compliant payment acceptance. Card data fields (PAN,
    expiry, CVV) must be AES-256 encrypted using the merchant's assigned
    encryption key before transmission. Raw card data must never be sent
    in plaintext.

    All requests must be signed using RSA-SHA256.
  contact:
    name: Simpaisa Platform Engineering
    url: https://www.simpaisa.com
    email: [email protected]
  license:
    name: Proprietary
    url: https://www.simpaisa.com/terms
  x-logo:
    url: https://www.simpaisa.com/logo.png

servers:
  - url: https://api.simpaisa.com
    description: Production
  - url: https://sandbox.simpaisa.com
    description: Sandbox

security:
  - rsaSha256Signature: []

paths:
  /v3/cards/{merchantId}/payments/initiate:
    post:
      operationId: initiateCardPayment
      summary: Initiate a card payment
      description: |
        Creates a new card payment authorisation. Card data fields must be
        AES-256 encrypted before submission. If the issuer requires 3-D Secure
        authentication, the response will include a challenge URL for customer
        redirection.
      tags:
        - Payments
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CardPaymentRequest'
      responses:
        '201':
          description: Payment initiated
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardPaymentResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
      callbacks:
        paymentStatusUpdate:
          '{$request.body#/callbackUrl}':
            post:
              summary: Payment status webhook
              requestBody:
                required: true
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/WebhookPayload'
              responses:
                '200':
                  description: Webhook acknowledged

  /v3/cards/{merchantId}/payments/{paymentId}/capture:
    post:
      operationId: captureCardPayment
      summary: Capture an authorised payment
      description: |
        Captures a previously authorised card payment. Supports full or partial
        capture. The capture amount must not exceed the authorised amount.
      tags:
        - Payments
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/PaymentIdPath'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CaptureRequest'
      responses:
        '200':
          description: Payment captured
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardPaymentResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v3/cards/{merchantId}/payments/{paymentId}/void:
    post:
      operationId: voidCardPayment
      summary: Void an authorised payment
      description: |
        Voids a previously authorised payment that has not yet been captured.
        The full authorisation hold is released on the cardholder's account.
      tags:
        - Payments
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/PaymentIdPath'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  description: Reason for voiding the authorisation
                  maxLength: 256
      responses:
        '200':
          description: Payment voided
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardPaymentResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v3/cards/{merchantId}/payments/{paymentId}/refund:
    post:
      operationId: refundCardPayment
      summary: Refund a captured payment
      description: |
        Initiates a full or partial refund on a captured card payment. The
        refund amount must not exceed the captured amount less any previous
        refunds.
      tags:
        - Payments
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/PaymentIdPath'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefundRequest'
      responses:
        '200':
          description: Refund initiated
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardPaymentResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v3/cards/{merchantId}/payments/{paymentId}:
    get:
      operationId: getCardPayment
      summary: Retrieve a card payment
      description: Returns the current state of a card payment.
      tags:
        - Payments
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/PaymentIdPath'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      responses:
        '200':
          description: Payment retrieved
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardPaymentResponse'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v3/cards/{merchantId}/payments/{paymentId}/3ds-challenge:
    post:
      operationId: submit3DSChallenge
      summary: Submit 3-D Secure challenge response
      description: |
        Submits the result of a 3-D Secure authentication challenge. Called
        after the cardholder completes the issuer's authentication flow and
        is redirected back to the merchant.
      tags:
        - 3-D Secure
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/PaymentIdPath'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - threeDSResult
              properties:
                threeDSResult:
                  type: string
                  description: Base64-encoded 3DS authentication result from the issuer
                threeDSVersion:
                  type: string
                  enum:
                    - '1.0'
                    - '2.1'
                    - '2.2'
                  description: 3-D Secure protocol version used
      responses:
        '200':
          description: 3DS challenge processed
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ThreeDSChallengeResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

components:
  securitySchemes:
    rsaSha256Signature:
      type: apiKey
      in: header
      name: X-Signature
      description: |
        RSA-SHA256 signature computed over the canonical request string.

  parameters:
    MerchantIdPath:
      name: merchantId
      in: path
      required: true
      description: Unique merchant identifier
      schema:
        type: string
        example: MCH-00123

    PaymentIdPath:
      name: paymentId
      in: path
      required: true
      description: Unique payment identifier
      schema:
        type: string
        format: uuid

    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: Client-generated unique key for idempotent processing
      schema:
        type: string
        format: uuid

    XMerchantId:
      name: X-Merchant-Id
      in: header
      required: true
      schema:
        type: string

    XTimestamp:
      name: X-Timestamp
      in: header
      required: true
      schema:
        type: string
        format: date-time

    XSignature:
      name: X-Signature
      in: header
      required: true
      schema:
        type: string

  headers:
    XTraceId:
      description: Unique trace identifier
      schema:
        type: string
        format: uuid
    XRateLimitLimit:
      description: Maximum requests per window
      schema:
        type: integer
    XRateLimitRemaining:
      description: Requests remaining in current window
      schema:
        type: integer
    XRateLimitReset:
      description: Unix epoch when rate limit resets
      schema:
        type: integer

  schemas:
    CardPaymentRequest:
      type: object
      required:
        - amount
        - currency
        - encryptedCardData
        - merchantTransactionId
      properties:
        amount:
          type: number
          format: double
          description: Payment amount
          example: 5000.00
        currency:
          type: string
          pattern: '^[A-Z]{3}$'
          description: ISO 4217 currency code
          example: PKR
        encryptedCardData:
          type: object
          required:
            - encryptedPan
            - encryptedExpiry
            - encryptedCvv
            - keyId
          properties:
            encryptedPan:
              type: string
              description: |
                AES-256 encrypted card PAN. The merchant must encrypt the
                primary account number using their assigned AES encryption
                key before transmission. Raw card numbers must never be
                sent in plaintext.
            encryptedExpiry:
              type: string
              description: AES-256 encrypted card expiry date (MM/YY format before encryption)
            encryptedCvv:
              type: string
              description: AES-256 encrypted card verification value
            keyId:
              type: string
              description: Identifier of the encryption key used
              example: key-2026-04
          description: |
            Encrypted card data envelope. All card fields must be AES-256
            encrypted using the merchant's assigned encryption key.
        cardholderName:
          type: string
          description: Name as printed on the card
          example: AHMED KHAN
        captureMode:
          type: string
          enum:
            - AUTO
            - MANUAL
          default: AUTO
          description: |
            AUTO captures immediately upon authorisation. MANUAL requires
            a separate capture call.
        threeDSRequired:
          type: boolean
          default: true
          description: Whether to enforce 3-D Secure authentication
        merchantTransactionId:
          type: string
          maxLength: 64
          example: CARD-2026-00789
        customerEmail:
          type: string
          format: email
          description: Customer email for receipt delivery
        customerIp:
          type: string
          description: Customer IP address for fraud scoring
        callbackUrl:
          type: string
          format: uri
          description: URL for webhook notifications
        returnUrl:
          type: string
          format: uri
          description: URL for 3DS redirect return
        metadata:
          type: object
          additionalProperties:
            type: string

    CardPaymentResponse:
      type: object
      required:
        - paymentId
        - merchantTransactionId
        - status
        - amount
        - currency
        - traceId
      properties:
        paymentId:
          type: string
          format: uuid
        merchantTransactionId:
          type: string
        status:
          type: string
          enum:
            - INITIATED
            - THREE_DS_REQUIRED
            - AUTHORISED
            - CAPTURED
            - PARTIALLY_CAPTURED
            - VOIDED
            - REFUNDED
            - PARTIALLY_REFUNDED
            - DECLINED
            - FAILED
            - EXPIRED
        amount:
          type: number
          format: double
          description: Authorised amount
        capturedAmount:
          type: number
          format: double
          description: Amount captured so far
        refundedAmount:
          type: number
          format: double
          description: Total amount refunded
        currency:
          type: string
        cardBrand:
          type: string
          enum:
            - VISA
            - MASTERCARD
            - UNIONPAY
          description: Detected card network
        maskedPan:
          type: string
          description: Masked card number showing last four digits
          example: '****1234'
        cardType:
          type: string
          enum:
            - CREDIT
            - DEBIT
            - PREPAID
        issuingBank:
          type: string
          description: Name of the card-issuing bank
        issuingCountry:
          type: string
          description: ISO 3166-1 alpha-2 country code of the issuer
        threeDSUrl:
          type: string
          format: uri
          description: 3DS challenge URL for customer redirection (when status is THREE_DS_REQUIRED)
        threeDSVersion:
          type: string
          description: 3-D Secure protocol version
        authCode:
          type: string
          description: Authorisation code from the card network
        acquirerTransactionId:
          type: string
          description: Reference from the acquiring bank
        declineReason:
          type: string
          description: Reason for decline, if applicable
        completedAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        metadata:
          type: object
          additionalProperties:
            type: string
        traceId:
          type: string
          format: uuid

    CaptureRequest:
      type: object
      required:
        - amount
      properties:
        amount:
          type: number
          format: double
          description: Amount to capture; must not exceed the authorised amount
          example: 5000.00
        finalCapture:
          type: boolean
          default: true
          description: |
            Whether this is the final capture. Set to false for partial
            captures when further captures are expected.

    RefundRequest:
      type: object
      required:
        - amount
        - reason
      properties:
        amount:
          type: number
          format: double
          description: Refund amount; must not exceed captured minus already refunded
          example: 2000.00
        reason:
          type: string
          description: Reason for the refund
          maxLength: 256
          example: Goods returned

    ThreeDSChallengeResponse:
      type: object
      required:
        - paymentId
        - status
        - threeDSAuthenticated
        - traceId
      properties:
        paymentId:
          type: string
          format: uuid
        status:
          type: string
          enum:
            - AUTHORISED
            - DECLINED
            - FAILED
          description: Payment status after 3DS authentication
        threeDSAuthenticated:
          type: boolean
          description: Whether the cardholder was successfully authenticated
        threeDSVersion:
          type: string
        authCode:
          type: string
          description: Authorisation code (present when status is AUTHORISED)
        declineReason:
          type: string
          description: Decline reason (present when status is DECLINED)
        traceId:
          type: string
          format: uuid

    WebhookPayload:
      type: object
      required:
        - event
        - paymentId
        - merchantTransactionId
        - status
        - timestamp
        - signature
      properties:
        event:
          type: string
          enum:
            - PAYMENT_AUTHORISED
            - PAYMENT_CAPTURED
            - PAYMENT_DECLINED
            - PAYMENT_VOIDED
            - PAYMENT_REFUNDED
            - PAYMENT_FAILED
            - THREE_DS_COMPLETED
        paymentId:
          type: string
          format: uuid
        merchantTransactionId:
          type: string
        status:
          type: string
        amount:
          type: number
          format: double
        currency:
          type: string
        maskedPan:
          type: string
        authCode:
          type: string
        declineReason:
          type: string
        timestamp:
          type: string
          format: date-time
        signature:
          type: string
          description: RSA-SHA256 signature for webhook verification

    ErrorResponse:
      type: object
      required:
        - error
        - traceId
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              example: CARD_DECLINED
            message:
              type: string
              example: The card was declined by the issuing bank.
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                  reason:
                    type: string
        traceId:
          type: string
          format: uuid

  responses:
    BadRequest:
      description: Invalid request parameters
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    Unauthorised:
      description: Authentication failed
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    NotFound:
      description: Resource not found
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    Conflict:
      description: Duplicate Idempotency-Key with different payload
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    UnprocessableEntity:
      description: Request understood but cannot be processed
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    TooManyRequests:
      description: Rate limit exceeded
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
        X-RateLimit-Limit:
          $ref: '#/components/headers/XRateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/XRateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/XRateLimitReset'
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    InternalServerError:
      description: Unexpected server error
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

tags:
  - name: Payments
    description: Card payment lifecycle (authorise, capture, void, refund)
  - name: 3-D Secure
    description: 3-D Secure authentication challenge flows