openapi: 3.1.0
info:
  title: DuitDigital Merchant API
  version: 1.0.0
  description: Public server-to-server API for approved DuitDigital merchants.
servers:
  - url: https://api.duitdigital.id
paths:
  /v1/merchant/payments:
    get:
      tags: [Merchant Payments]
      security: [{merchantBasic: []}]
      responses: {'200': {description: Merchant payment list}}
    post:
      tags: [Merchant Payments]
      security: [{merchantBasic: []}]
      parameters: [{name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 190}}]
      requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/CreatePaymentRequest'}}}}
      responses: {'201': {description: Payment created}, '409': {description: Idempotency or route conflict}}
  /v1/merchant/payments/{payment}/refunds:
    post:
      tags: [Refunds]
      security: [{merchantBasic: []}]
      parameters:
        - {name: payment, in: path, required: true, schema: {type: string}}
        - {name: Idempotency-Key, in: header, required: true, schema: {type: string}}
      responses: {'201': {description: Refund requested}, '409': {description: Payment is not refundable}}
  /v1/merchant/payments/{payment}:
    get:
      tags: [Merchant Payments]
      security: [{merchantBasic: []}]
      parameters: [{name: payment, in: path, required: true, schema: {type: string}}]
      responses: {'200': {description: Merchant-owned payment detail}, '404': {description: Payment not found}}
  /v1/merchant/payment-links:
    get: {tags: [Payment Links], security: [{merchantBasic: []}], responses: {'200': {description: Payment links}}}
    post: {tags: [Payment Links], security: [{merchantBasic: []}], responses: {'201': {description: Payment link created}}}
  /v1/merchant/payment-links/{link}:
    get:
      tags: [Payment Links]
      security: [{merchantBasic: []}]
      parameters: [{name: link, in: path, required: true, schema: {type: string}}]
      responses: {'200': {description: Merchant-owned payment link}, '404': {description: Payment link not found}}
  /v1/merchant/payment-links/{link}/activate:
    post:
      tags: [Payment Links]
      security: [{merchantBasic: []}]
      parameters: [{name: link, in: path, required: true, schema: {type: string}}]
      responses: {'200': {description: Payment link activated}, '409': {description: Link cannot be activated}}
  /v1/merchant/payment-links/{link}/deactivate:
    post:
      tags: [Payment Links]
      security: [{merchantBasic: []}]
      parameters: [{name: link, in: path, required: true, schema: {type: string}}]
      responses: {'200': {description: Payment link deactivated}}
  /v1/merchant/otp/send:
    post:
      tags: [OTP]
      security: [{merchantBasic: []}]
      parameters: [{name: Idempotency-Key, in: header, required: true, schema: {type: string}}]
      responses: {'201': {description: OTP queued}, '409': {description: Rate limited or provider unavailable}}
  /v1/merchant/otp/verify:
    post: {tags: [OTP], security: [{merchantBasic: []}], responses: {'200': {description: OTP verified}, '409': {description: Invalid or expired OTP}}}
  /v1/merchant/checkout-sessions:
    post:
      tags: [Checkout]
      security: [{merchantBasic: []}]
      responses: {'201': {description: Checkout created}}
  /v1/merchant/ppob/products:
    get: {tags: [PPOB], security: [{merchantBasic: []}], responses: {'200': {description: Active PPOB products}}}
  /v1/merchant/ppob/orders:
    post:
      tags: [PPOB]
      security: [{merchantBasic: []}]
      parameters: [{name: Idempotency-Key, in: header, required: true, schema: {type: string}}]
      responses: {'201': {description: PPOB order accepted}, '409': {description: Provider unavailable or order conflict}}
  /v1/merchant/ppob/orders/{order}:
    get:
      tags: [PPOB]
      security: [{merchantBasic: []}]
      parameters: [{name: order, in: path, required: true, schema: {type: string}}]
      responses: {'200': {description: PPOB order detail}, '404': {description: Order not found}}
  /v1/merchant/smm/services:
    get: {tags: [SMM], security: [{merchantBasic: []}], responses: {'200': {description: Active SMM services}}}
  /v1/merchant/smm/orders:
    post:
      tags: [SMM]
      security: [{merchantBasic: []}]
      parameters: [{name: Idempotency-Key, in: header, required: true, schema: {type: string}}]
      responses: {'201': {description: SMM order accepted}, '409': {description: Provider unavailable or order conflict}}
  /v1/merchant/smm/orders/{order}:
    get:
      tags: [SMM]
      security: [{merchantBasic: []}]
      parameters: [{name: order, in: path, required: true, schema: {type: string}}]
      responses: {'200': {description: SMM order detail}, '404': {description: Order not found}}
  /v1/merchant/reports:
    get: {tags: [Reports], security: [{merchantBasic: []}], responses: {'200': {description: Merchant aggregate report}}}
components:
  securitySchemes:
    merchantBasic: {type: http, scheme: basic}
  responses:
    ValidationError: {description: Validation failed}
  schemas:
    CreatePaymentRequest:
      type: object
      required: [reference,amount,currency,channel,customer]
      properties:
        reference: {type: string}
        amount: {oneOf: [{type: integer}, {type: string, pattern: '^[0-9]+(\\.[0-9]{1,2})?$'}]}
        currency: {type: string, default: IDR}
        channel: {type: string}
        customer: {type: object, required: [name,email], properties: {name: {type: string}, email: {type: string, format: email}, phone: {type: string}}}
