{
  "openapi": "3.0.3",
  "info": {
    "title": "Kryz Pay Merchant REST API",
    "version": "1.0.0",
    "description": "Programmatic collection and automated notification matching for Touch 'n Go eWallet payments in Malaysia. All requests require an active Bearer API key.",
    "contact": {
      "name": "Kryz Pay Developer Support",
      "url": "https://wa.me/60137345871"
    },
    "license": {
      "name": "Proprietary - Kryz Pay Gateway"
    }
  },
  "servers": [
    {
      "url": "https://pay.kryznet.com/api/v1",
      "description": "Production Gateway (v1)"
    },
    {
      "url": "https://pay.kryznet.com/api",
      "description": "Legacy Alias"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "paths": {
    "/payments": {
      "post": {
        "summary": "Create Payment Session",
        "description": "Allocates a unique collision-free sen offset (1–99 sen) for the requested base ringgit amount and creates a 10-minute customer checkout session.",
        "operationId": "createPayment",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Unique client order key to prevent duplicate allocations during network retries.",
            "schema": {
              "type": "string",
              "example": "ORDER-1234"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount_sen": {
                    "type": "integer",
                    "description": "Base order amount in integer sen (e.g. 1200 for RM 12.00). If omitted, an open amount is allocated.",
                    "example": 1200
                  },
                  "order_ref": {
                    "type": "string",
                    "description": "Internal merchant order ID or invoice reference.",
                    "example": "TOPUP-1234"
                  },
                  "expected_payer": {
                    "type": "string",
                    "description": "Optional customer name or phone identifier for matching logs.",
                    "example": "Ahmad"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay: original payment returned without charging a new offset.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePaymentResponse"
                }
              }
            }
          },
          "201": {
            "description": "Payment session created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePaymentResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error (e.g. amount_sen must be a positive integer).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: missing or invalid Bearer API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: API key lacks payments:write scope, or merchant account is pending verification.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Offset pool exhausted: all 99 sen slots near this amount are currently occupied. Retry after pending sessions expire.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List Merchant Payments",
        "description": "Retrieves historical payments scoped to the authenticated merchant account.",
        "operationId": "listPayments",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter payments by status.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "received",
                "expired"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Number of payments to return (1-200, default 50).",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of payments.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/payments/{reference}": {
      "get": {
        "summary": "Get Payment Status",
        "description": "Fetches details and real-time status of a single payment by reference or ID.",
        "operationId": "getPayment",
        "parameters": [
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "description": "Payment reference code (e.g. PG-6C5CB378) or database ID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Payment details.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payment": {
                      "$ref": "#/components/schemas/Payment"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Payment not found or belongs to another merchant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/events": {
      "get": {
        "summary": "Merchant Diagnostic Events Stream",
        "description": "Audit log of incoming eWallet notification matching events scoped to this merchant.",
        "operationId": "listEvents",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of recent matching events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "amount_myr": {
                            "type": "string",
                            "example": "12.28"
                          },
                          "direction": {
                            "type": "string",
                            "example": "in"
                          },
                          "outcome": {
                            "type": "string",
                            "example": "matched"
                          },
                          "matched": {
                            "type": "boolean",
                            "example": true
                          },
                          "notified_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "received_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/webhook-deliveries": {
      "get": {
        "summary": "List Webhook Deliveries",
        "description": "Audit log of signed HTTP webhook delivery attempts sent to your configured URL.",
        "operationId": "listDeliveries",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent webhook deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "integer"
                          },
                          "event_type": {
                            "type": "string",
                            "example": "payment.received"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "delivered",
                              "failed"
                            ]
                          },
                          "attempts": {
                            "type": "integer",
                            "example": 1
                          },
                          "response_status": {
                            "type": "integer",
                            "example": 200
                          },
                          "created_at": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "kpt_live_..."
      }
    },
    "schemas": {
      "Payment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "a1b2c3d4e5f6"
          },
          "reference": {
            "type": "string",
            "example": "PG-6C5CB378"
          },
          "amount_sen": {
            "type": "integer",
            "description": "Exact amount customer must transfer including offset sen.",
            "example": 1228
          },
          "amount_myr": {
            "type": "string",
            "description": "Ringgit formatted transfer amount.",
            "example": "12.28"
          },
          "base_amount_sen": {
            "type": "integer",
            "description": "Merchant order base price.",
            "example": 1200
          },
          "base_amount_myr": {
            "type": "string",
            "description": "Ringgit formatted base price.",
            "example": "12.00"
          },
          "offset_sen": {
            "type": "integer",
            "description": "Allocated unique collision-prevention fee in sen.",
            "example": 28
          },
          "offset_myr": {
            "type": "string",
            "description": "Ringgit formatted offset fee.",
            "example": "0.28"
          },
          "currency": {
            "type": "string",
            "example": "MYR"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "received",
              "expired"
            ],
            "example": "pending"
          },
          "verification": {
            "type": "string",
            "description": "Deterministic confirmation signal source.",
            "example": "notification_only"
          },
          "order_ref": {
            "type": "string",
            "example": "TOPUP-1234"
          },
          "expected_payer": {
            "type": "string",
            "example": "Ahmad"
          },
          "api_version": {
            "type": "string",
            "example": "v1"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "qr_image": {
            "type": "string",
            "format": "uri",
            "description": "Direct public URL to the payment QR code image",
            "example": "https://pay.kryznet.com/qr/0debedcfda207cbafcb9e3673cd16751.jpg"
          },
          "qr_url": {
            "type": "string",
            "format": "uri",
            "description": "Dedicated QR endpoint for this payment session",
            "example": "https://pay.kryznet.com/pay/PG-6C5CB378/qr"
          },
          "qr_data_url": {
            "type": "string",
            "description": "Inline Base64 Data URI for direct embedding without secondary requests",
            "example": "data:image/jpeg;base64,/9j/4AAQ..."
          }
        }
      },
      "CreatePaymentResponse": {
        "type": "object",
        "properties": {
          "payment": {
            "$ref": "#/components/schemas/Payment"
          },
          "idempotent_replay": {
            "type": "boolean",
            "example": false
          },
          "pay_url": {
            "type": "string",
            "format": "uri",
            "example": "https://pay.kryznet.com/pay/PG-6C5CB378"
          },
          "qr_image": {
            "type": "string",
            "format": "uri",
            "description": "Direct public URL to the payment QR code image",
            "example": "https://pay.kryznet.com/qr/0debedcfda207cbafcb9e3673cd16751.jpg"
          },
          "qr_url": {
            "type": "string",
            "format": "uri",
            "description": "Dedicated QR endpoint for this payment session",
            "example": "https://pay.kryznet.com/pay/PG-6C5CB378/qr"
          },
          "qr_data_url": {
            "type": "string",
            "description": "Inline Base64 Data URI of the payment QR image",
            "example": "data:image/jpeg;base64,/9j/4AAQ..."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "message": {
                "type": "string",
                "example": "no unique temporary amount available near amount_sen; retry shortly"
              },
              "status": {
                "type": "integer",
                "example": 409
              }
            }
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "payment.received",
              "payment.expired"
            ],
            "example": "payment.received"
          },
          "api_version": {
            "type": "string",
            "example": "v1"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-10-10T08:00:00.000Z"
          },
          "data": {
            "type": "object",
            "properties": {
              "payment": {
                "$ref": "#/components/schemas/Payment"
              }
            }
          }
        }
      }
    }
  }
}