{"openapi":"3.0.0","info":{"title":"Secure QRIS API","version":"1.0.0","description":"A secure, multi-tenant API for generating QRIS codes and processing payment webhooks. \n\n**AUTHENTICATION:** \n1. Obtain `clientId` and `clientSecret` via `/register` (Admin only).\n2. Send `X-Client-ID` header with every request.\n3. **IMPORTANT:** The `clientSecret` is **NEVER** sent to the server. It is used exclusively to **ENCRYPT** your request body and **DECRYPT** the server response.\n\n### Encryption Flow:\n1. Generate random 16-byte salt and 12-byte IV.\n2. Derive Key: PBKDF2(ClientSecret, Salt, 100k iters, SHA-256) -> derive AES-GCM 256 key.\n3. Encrypt JSON payload string -> Ciphertext.\n4. Send JSON: `{ \"salt\": \"hex\", \"iv\": \"hex\", \"content\": \"hex\" }`."},"servers":[{"url":"https://qris-service.anda.com","description":"Production Server"},{"url":"http://localhost:8787","description":"Local Development"}],"paths":{"/register":{"post":{"summary":"Register New Client","description":"Registers a new client with a custom User Key and existing QRIS String. Returns the unique Client ID and generated Secret Key.","security":[{"bearerAuth":[]}],"parameters":[{"in":"header","name":"Authorization","schema":{"type":"string"},"required":true,"description":"Bearer <ADMIN_SECRET>"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_key":{"type":"string","description":"User's private password/key"},"qr_string":{"type":"string","description":"Raw string content of the static QRIS"}},"required":["user_key","qr_string"]}}}},"responses":{"200":{"description":"Credentials Generated","content":{"application/json":{"example":{"clientId":"uuid-...","clientSecret":"hex-string...","message":"Client Registered. Secret generated from (UserKey + AdminSecret)."}}}},"403":{"description":"Unauthorized (Admin Secret Required)"}}}},"/generate":{"post":{"summary":"Generate Dynamic QRIS","description":"Generates a dynamic QRIS code using the client's stored QR string. Requires `X-Client-ID` header and encrypted payload.\n\n**Plaintext sebelum di-enkripsi:** `{ \"amount\": number, \"ref_id?\": string }`\n- `amount` (required) — nominal IDR\n- `ref_id` (optional) — kustom unique code untuk embedded di QRIS. Jika tidak diisi, sistem generate 6 karakter acak.","parameters":[{"in":"header","name":"X-Client-ID","schema":{"type":"string"},"required":true,"description":"Client ID obtained from registration"}],"requestBody":{"required":true,"description":"Encrypted JSON of: `{ \"amount\": number, \"ref_id?\": string }`. ref_id optional — jika ada, dipakai sebagai unique_code QRIS.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EncryptedPayload"}}}},"responses":{"200":{"description":"Successful Generation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EncryptedPayload"},"example":{"note":"Decrypted content will be: `{ \"qris\": \"...\", \"qr_image_svg\": \"<svg>...\", \"unique_code\": \"ABC123\" }`. Jika ref_id dikirim, unique_code = ref_id."}}}},"401":{"description":"Decryption Failed / Invalid Passphrase"}}}},"/webhook":{"post":{"summary":"Payment Webhook","description":"Receives encrypted payment notifications.","requestBody":{"required":true,"description":"Encrypted JSON of arbitrary payment data (e.g. amount, sender).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EncryptedPayload"}}}},"responses":{"200":{"description":"Webhook Processed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EncryptedPayload"}}}}}}}},"components":{"schemas":{"EncryptedPayload":{"type":"object","properties":{"salt":{"type":"string","description":"16-byte random salt (Hex string)","example":"a1b2c3d4..."},"iv":{"type":"string","description":"12-byte random IV (Hex string)","example":"12345678..."},"content":{"type":"string","description":"AES-GCM encrypted ciphertext (Hex string)","example":"deadbeef..."}},"required":["salt","iv","content"]}}}}