openapi: 3.1.0
info:
  title: FlyPay UTpay-Compatible Merchant API
  version: 1.0.0
  description: Exact migration contract for existing UTpay merchant integrations. Amounts are full IDR, and application errors use HTTP 200 with body code 400.
servers:
  - url: https://api.flypay.asia
    description: Production merchant API
paths:
  /api/order/create:
    post:
      summary: Create QRIS order
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/CreateOrder'}
      responses:
        '200': {$ref: '#/components/responses/LegacyResponse'}
  /api/order/query:
    post:
      summary: Query order by merchant order ID
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/QueryOrder'}
      responses:
        '200': {$ref: '#/components/responses/LegacyResponse'}
  /api/disbursement/get-balance:
    post:
      summary: Get wallet balance
      parameters: [{$ref: '#/components/parameters/XSignature'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/ClientBody'}
      responses:
        '200': {$ref: '#/components/responses/LegacyResponse'}
  /api/disbursement/create:
    post:
      summary: Create bank disbursement
      parameters: [{$ref: '#/components/parameters/XSignature'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/CreateBankDisbursement'}
      responses:
        '200': {$ref: '#/components/responses/LegacyResponse'}
  /api/disbursement/create-ewallet:
    post:
      summary: Create e-wallet disbursement
      parameters: [{$ref: '#/components/parameters/XSignature'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/CreateEwalletDisbursement'}
      responses:
        '200': {$ref: '#/components/responses/LegacyResponse'}
  /api/disbursement/query:
    post:
      summary: Query disbursement by merchant order ID
      parameters: [{$ref: '#/components/parameters/XSignature'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/QueryDisbursement'}
      responses:
        '200': {$ref: '#/components/responses/LegacyResponse'}
components:
  parameters:
    XSignature:
      in: header
      name: X-SIGNATURE
      required: true
      schema: {type: string}
      description: Base64 RSA-SHA256 signature over clientId|clientSecret|sha256(minified JSON body).
  schemas:
    ClientBody:
      type: object
      required: [clientId]
      properties:
        clientId: {type: string}
    CreateOrder:
      type: object
      required: [clientId, orderId, totalAmount, paymentType, signature, customerName, customerEmail, customerPhone]
      properties:
        clientId: {type: string}
        orderId: {type: string}
        totalAmount: {type: integer, minimum: 1, description: Full IDR}
        paymentType: {type: string, enum: [QRIS]}
        signature: {type: string, description: Lowercase SHA-512 hex}
        customerName: {type: string}
        customerEmail: {type: string, format: email}
        customerPhone: {type: string, pattern: '^62[0-9]+$'}
        notifyUrl: {type: string, format: uri}
        returnUrl: {type: string, format: uri}
    QueryOrder:
      type: object
      required: [clientId, orderId, signature]
      properties:
        clientId: {type: string}
        orderId: {type: string}
        signature: {type: string}
    CreateBankDisbursement:
      type: object
      required: [clientId, orderId, bankCode, accountNumber, accountName, requestAmount]
      properties:
        clientId: {type: string}
        orderId: {type: string}
        bankCode: {type: string}
        accountNumber: {type: string}
        accountName: {type: string}
        requestAmount: {type: integer, description: Full IDR}
        notifyUrl: {type: string, format: uri}
    CreateEwalletDisbursement:
      type: object
      required: [clientId, orderId, ewalletCode, accountNumber, requestAmount]
      properties:
        clientId: {type: string}
        orderId: {type: string}
        ewalletCode: {type: string, enum: [OVO, GOPAY, GOPAYDRIVER, SHOPEEPAY, LINKAJA, DANA, KASPRO]}
        accountNumber: {type: string, pattern: '^08[0-9]+$'}
        requestAmount: {type: integer, description: Full IDR}
        notifyUrl: {type: string, format: uri}
    QueryDisbursement:
      allOf:
        - {$ref: '#/components/schemas/ClientBody'}
        - type: object
          required: [orderId]
          properties:
            orderId: {type: string}
    LegacyEnvelope:
      type: object
      required: [code, message, data]
      properties:
        code: {type: integer, enum: [200, 400]}
        message: {type: string}
        data: {type: [object, 'null'], additionalProperties: true}
  responses:
    LegacyResponse:
      description: UTpay-compatible success or application error envelope.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/LegacyEnvelope'}
