Kontrak API FPDP (Bahasa Indonesia)

1. Cakupan dan status

Dokumen ini mendefinisikan kontrak HTTP target untuk MVP FPDP. Ini adalah kontrak desain; repository saat ini belum mengimplementasikan sebagian besar route. Sumber machine-readable tersedia pada <code>openapi.yaml</code>.

Base path: /api/v1

Autentikasi:

- pembacaan data publik tidak membutuhkan autentikasi;

- operasi owner/admin memakai Authorization: Bearer <token>;

- webhook pembayaran memakai signature khusus provider dan tidak memakai bearer token pengguna.

Content type: application/json, kecuali body webhook dapat mengikuti format JSON provider.

2. Konvensi umum

- ID yang diekspos API berupa UUID string.

- Timestamp memakai RFC 3339 UTC.

- Nilai uang berupa decimal string dan kode currency ISO 4217 untuk mencegah kesalahan floating point.

- Collection memakai cursor pagination: data, meta.next_cursor, dan meta.has_more.

- Resource mutable mengembalikan ETag ketika optimistic concurrency tersedia.

- Setiap konten mengidentifikasi source_type, source_provider, dan canonical_url.

- Retry pembuatan pembayaran harus memakai header Idempotency-Key yang sama.

Envelope sukses standar:

{"data": {}, "meta": {"request_id": "req_..."}}

Envelope error standar:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Permintaan tidak valid.",
    "details": [{"field": "email", "reason": "invalid_format"}],
    "request_id": "req_..."
  }
}

3. Daftar endpoint

Sistem dan autentikasi

| Method | Path | Auth | Kegunaan |

|---|---|---|---|

| GET | /health | Publik | Status hidup aplikasi dan ringkasan dependency |

| POST | /auth/register | Publik | Mendaftarkan owner dan node awal |

| POST | /auth/login | Publik | Menukar credential dengan access token |

| POST | /auth/logout | Bearer | Mencabut token aktif |

| GET | /me | Bearer | Mengambil user dan konteks node aktif |

Profil dan konten

| Method | Path | Auth | Kegunaan |

|---|---|---|---|

| GET | /profiles/{handle} | Publik | Membaca profil publik |

| PATCH | /me/profile | Bearer | Memperbarui profil owner |

| GET | /posts | Publik | Daftar post dengan filter sumber/visibility |

| POST | /posts | Bearer | Membuat post lokal |

| GET | /posts/{postId} | Publik | Membaca post yang dapat dilihat |

| PATCH | /posts/{postId} | Bearer | Memperbarui post lokal milik user |

| DELETE | /posts/{postId} | Bearer | Soft-delete post lokal milik user |

Penulisan post menerima maksimal 10 metadata media terurut (IMAGE, VIDEO, AUDIO, atau FILE). URL media wajib berupa URL HTTPS absolut tanpa credential tertanam; alternative text dibatasi 500 karakter. Mengirim media melalui PATCH mengganti seluruh daftar media secara atomik.

| GET | /timeline | Publik/Bearer opsional | Timeline normalisasi local, external, dan federated |

Sumber eksternal

| Method | Path | Auth | Kegunaan |

|---|---|---|---|

| GET | /external-sources | Bearer | Daftar koneksi dan kondisi sinkronisasi |

| POST | /external-sources/test | Bearer | Validasi dan preview tanpa menyimpan sumber |

| POST | /external-sources | Bearer | Menyimpan dan menjadwalkan sumber |

| GET | /external-sources/{sourceId} | Bearer | Membaca konfigurasi dan kondisi sumber |

| PATCH | /external-sources/{sourceId} | Bearer | Mengubah interval, visibility, atau status enabled |

| DELETE | /external-sources/{sourceId} | Bearer | Memutus sumber |

| POST | /external-sources/{sourceId}/sync | Bearer | Memasukkan sinkronisasi manual ke antrean |

Produk, order, dan pembayaran

| Method | Path | Auth | Kegunaan |

|---|---|---|---|

| GET | /products | Publik | Daftar produk tersedia |

| POST | /products | Bearer | Membuat produk lokal |

| GET | /products/{productId} | Publik | Membaca detail produk |

| PATCH | /products/{productId} | Bearer | Memperbarui produk milik user |

| POST | /orders | Publik/Bearer opsional | Membuat order dan snapshot total immutable |

| GET | /orders/{orderId} | Bearer/order token | Membaca order |

| POST | /orders/{orderId}/payments | Bearer/order token | Membuat payment attempt; wajib Idempotency-Key |

| GET | /payments/{paymentId} | Bearer/order token | Membaca status pembayaran ternormalisasi |

| POST | /payments/{paymentId}/cancel | Bearer/order token | Membatalkan pembayaran pending |

| POST | /payments/{paymentId}/refunds | Admin Bearer | Meminta refund penuh atau sebagian |

| POST | /webhooks/payments/{gatewayCode} | Signature | Menerima dan menormalisasi event provider |

Administrasi dan federasi

| Method | Path | Auth | Kegunaan |

|---|---|---|---|

| GET | /admin/payment-gateways | Admin Bearer | Daftar kapabilitas dan status konfigurasi gateway |

| PUT | /admin/payment-gateways/{gatewayCode} | Admin Bearer | Mengatur dan mengaktifkan gateway |

| GET | /admin/integration-jobs | Admin Bearer | Memeriksa queue sinkronisasi dan kegagalan |

| POST | /admin/integration-jobs/{jobId}/retry | Admin Bearer | Mengulang job gagal |

| GET | /.well-known/fpdp | Publik | Discovery identitas dan kapabilitas node |

4. Perilaku penting

Pengujian dan penyimpanan sumber

POST /external-sources/test melakukan fetch server-side yang dibatasi, memblokir target network privat/reserved, menerapkan timeout dan batas ukuran, kemudian memberikan preview yang sudah dinormalisasi. Penyimpanan sumber merupakan aksi eksplisit yang terpisah.

Asal-usul timeline

Setiap item memuat:

- source_type: LOCAL, EXTERNAL, atau FEDERATED;

- source_provider: misalnya FPDP, RSS, ATOM, CUSTOM_API;

- canonical_url: URL original yang otoritatif;

- identitas author dan waktu publikasi.

Idempotency pembayaran dan webhook

- Client menggunakan satu Idempotency-Key untuk retry permintaan pembayaran logis yang sama.

- Server mengembalikan hasil pertama ketika key dan payload sama.

- Key yang sama dengan payload berbeda menghasilkan 409 IDEMPOTENCY_CONFLICT.

- Webhook diverifikasi sebelum diproses dan dideduplikasi berdasarkan provider + event ID.

- Webhook duplikat yang valid menghasilkan 200 tanpa menjalankan transisi dua kali.

Status code yang disarankan

| Status | Arti |

|---|---|

| 200 | Pembacaan/update berhasil atau webhook duplikat diterima |

| 201 | Resource berhasil dibuat |

| 202 | Sinkronisasi/refund async diterima |

| 204 | Logout atau penghapusan selesai |

| 400 | Request malformed |

| 401 | Autentikasi atau signature webhook tidak ada/tidak valid |

| 403 | Terautentikasi tetapi tidak memiliki izin |

| 404 | Resource tidak ada atau tidak dapat dilihat |

| 409 | Konflik state atau idempotency |

| 422 | Validasi semantik gagal |

| 429 | Rate limit terlampaui |

| 502 | Provider upstream gagal |