openapi: 3.0.3
info:
  title: YOUPI PG V2 — Public Developer Integration API
  version: 2.0.0
  description: |
    Official public integration API and webhook contract for YOUPI PG V2 Sovereign Payment Gateway.
    Allows merchants to create payment sessions, query verification status, and receive authoritative HMAC-SHA256 signed webhooks.

servers:
  - url: http://localhost:7000
    description: Local Checkout Portal (Transparent API Proxy with Zero-CORS)
  - url: http://localhost:6100
    description: Local Payment Core API (Direct Service Port)
  - url: https://pay.yourdomain.com
    description: Production Gateway Base URL

paths:
  /api/v1/payments:
    post:
      summary: Create Payment Session / Order
      description: |
        Initializes a new payment session. Returns the payment identifier and the checkoutUrl
        to be loaded inside the YOUPI modal iframe or standalone browser window.
      operationId: createPayment
      parameters:
        - name: X-Merchant-Id
          in: header
          required: false
          description: Public publishable merchant identifier. If omitted, uses the default configured merchant.
          schema:
            type: string
            example: "00000000-0000-0000-0000-000000000001"
        - name: Idempotency-Key
          in: header
          required: false
          description: Unique idempotency key preventing duplicate payment creation.
          schema:
            type: string
            example: "idem_9f82b71e"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePaymentRequest'
      responses:
        '201':
          description: Payment session successfully created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreatePaymentResponse'
        '200':
          description: Existing session returned for idempotent request or ongoing active session.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreatePaymentResponse'
        '400':
          description: Missing or invalid request parameters.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Access blocked due to security/fraud flagging on customer account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Duplicate active payment session detected on another device.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DuplicateSessionResponse'

  /api/v1/payments/{id}:
    get:
      summary: Get Payment Status
      description: |
        Retrieves current lifecycle status and outcome of a payment session.
      operationId: getPaymentStatus
      parameters:
        - name: id
          in: path
          required: true
          description: Unique payment session UUID.
          schema:
            type: string
            example: "3fa85f64-5717-4562-b3fc-2c963f66afa6"
      responses:
        '200':
          description: Payment details and outcome.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentDetailsResponse'
        '404':
          description: Payment session not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/checkout/{id}/cancel:
    post:
      summary: Cancel Payment Session
      description: |
        Explicitly cancels an active or queued payment session, immediately releasing the UPI concurrency slot.
      operationId: cancelPaymentSession
      parameters:
        - name: id
          in: path
          required: true
          description: Payment session UUID to cancel.
          schema:
            type: string
      responses:
        '200':
          description: Session transitioned to CANCELLED.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  status:
                    type: string
                    example: "CANCELLED"
        '404':
          description: Session not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  schemas:
    CreatePaymentRequest:
      type: object
      required:
        - orderId
        - amount
      properties:
        orderId:
          type: string
          description: Merchant order identifier.
          example: "ORDER-1001"
        amount:
          type: string
          description: Amount in Indian Rupees (INR) with up to 2 decimal places.
          example: "499.00"
        currency:
          type: string
          default: "INR"
          example: "INR"
        customer:
          type: object
          properties:
            name:
              type: string
              example: "Mohit Kumar"
            email:
              type: string
              example: "mohit@example.com"
            phoneNumber:
              type: string
              example: "9876543210"
        description:
          type: string
          example: "Order Payment #1001"
        returnUrl:
          type: string
          description: Merchant URL to redirect customer upon completion.
          example: "https://mystore.example/order-success?orderId=ORDER-1001"
        metadata:
          type: object
          description: Unmodified custom key-value pairs preserved by YOUPI PG.
          additionalProperties: true
          example:
            product_id: "TSHIRT-01"
            cart_id: "CART-88"

    CreatePaymentResponse:
      type: object
      properties:
        paymentId:
          type: string
          example: "c7942127-14e3-469a-9e4a-9359a16f903a"
        orderId:
          type: string
          example: "ORDER-1001"
        amount:
          type: string
          example: "499.00"
        amountPaise:
          type: string
          example: "49900"
        currency:
          type: string
          example: "INR"
        status:
          type: string
          enum: [INITIATED, ACTIVE, QUEUED, PROCESSING, SUCCESS, FAILED, EXPIRED, CANCELLED]
          example: "INITIATED"
        queuePosition:
          type: integer
          example: 0
        checkoutUrl:
          type: string
          example: "http://localhost:7000/checkout/c7942127-14e3-469a-9e4a-9359a16f903a"

    PaymentDetailsResponse:
      type: object
      properties:
        paymentId:
          type: string
          example: "c7942127-14e3-469a-9e4a-9359a16f903a"
        orderId:
          type: string
          example: "ORDER-1001"
        amount:
          type: string
          example: "499.00"
        amountPaise:
          type: string
          example: "49900"
        currency:
          type: string
          example: "INR"
        status:
          type: string
          enum: [INITIATED, ACTIVE, QUEUED, PROCESSING, SUCCESS, FAILED, EXPIRED, CANCELLED]
          example: "SUCCESS"
        method:
          type: string
          example: "UPI"
        verificationLevel:
          type: integer
          description: 0 = unverified, 1 = SMS observed, 2 = Dual SMS + Gmail verified.
          example: 2
        completedAt:
          type: string
          format: date-time
          nullable: true
          example: "2026-10-07T12:00:00.000Z"
        metadata:
          type: object
          additionalProperties: true

    WebhookPayload:
      type: object
      description: Payload delivered to merchant configured webhook URL via HTTPS POST.
      properties:
        event:
          type: string
          enum: [SUCCESS, FAILED, EXPIRED, CANCELLED]
          example: "SUCCESS"
        paymentId:
          type: string
          example: "c7942127-14e3-469a-9e4a-9359a16f903a"
        orderId:
          type: string
          example: "ORDER-1001"
        amount:
          type: string
          example: "499.00"
        amountPaise:
          type: integer
          example: 49900
        currency:
          type: string
          example: "INR"
        method:
          type: string
          example: "UPI"
        utr:
          type: string
          description: 12-digit bank reference number extracted by banking observers.
          example: "123456789012"
        completedAt:
          type: string
          format: date-time
          example: "2026-10-07T12:00:00.000Z"
        metadata:
          type: object
          additionalProperties: true

    DuplicateSessionResponse:
      type: object
      properties:
        error:
          type: string
          example: "DUPLICATE_SESSION_DETECTED"
        code:
          type: string
          example: "DEVICE_SESSION_MISMATCH"
        session_id:
          type: string
          example: "c7942127-14e3-469a-9e4a-9359a16f903a"
        action:
          type: string
          example: "END_SESSION_AND_CREATE_NEW"
        message:
          type: string

    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          example: "Missing required fields: orderId and amount."
        message:
          type: string
          nullable: true
