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