PSP Integration Contract
Kontrak teknis untuk PSP yang terintegrasi dengan platform Kesles Merchant.
Dokumen ini hanya untuk pihak yang sudah memiliki NDA dengan Kesles. Jangan disebarkan.
1. Prinsip Dasar
- Read-only — PSP hanya memiliki akses GET ke data merchant via Lookup API. Write endpoint (NMID assignment, payment events) memerlukan akses Tier-1 Internal.
- Data isolation — setiap bank hanya melihat merchant yang NMID-nya diterbitkan oleh acquirer tersebut. Bank A tidak bisa melihat merchant Bank B.
- Active-only — hanya merchant dengan
status = active,deleted_at IS NULL, dannmid IS NOT NULLyang dikembalikan. - Idempotent — semua request aman untuk di-retry (gunakan
X-Request-ID). - Eventual consistency — data bisa lag ≤30 detik.
2. Authentication — HMAC-SHA256
Header auth berbeda tergantung service yang dituju:
2.1 Lookup API — dashboard_api (port 8082)
Berlaku untuk: GET /api/psp/v1/merchants/*
X-API-Key-ID: <public key identifier — aman untuk di-log>
X-Timestamp: <unix seconds, waktu sekarang>
X-Signature: hmac-sha256=<computed signature>
X-Request-ID: <uuid v4 untuk tracing>
2.2 Events API — payment-service (port 8085)
Berlaku untuk: POST /psp/v1/events, POST /psp/v1/settlements
X-PSP-Key-ID: <public key identifier — aman untuk di-log>
X-PSP-Timestamp: <RFC3339 timestamp, waktu sekarang — contoh 2026-06-08T10:30:00+07:00>
X-PSP-Signature: <computed signature — raw hex, tanpa prefix>
X-Request-ID: <uuid v4 untuk tracing>
2.3 Cara Menghitung Signature
Lookup API — string-to-sign: {timestamp}\n{method}\n{path}\n{body}
Contoh untuk GET /api/psp/v1/merchants/by-nmid/ID102326873XXXX:
1716452580
GET
/api/psp/v1/merchants/by-nmid/ID102326873XXXX
(Body kosong untuk GET; trailing newline tetap ada)
Events API — string-to-sign: {method}\n{path}\n{timestamp}\n{body} — timestamp berformat RFC3339 (sama dengan header X-PSP-Timestamp).
Contoh untuk POST /psp/v1/events:
POST
/psp/v1/events
2026-06-08T10:30:00+07:00
{"external_event_id":"psp-evt-abc-123","event_type":"transaction.success",...}
Penting: urutan field di signed_payload berbeda antara dua service. Lookup API memakai unix seconds; Events API memakai RFC3339. Pastikan menggunakan format yang benar sesuai endpoint tujuan.
Compute HMAC-SHA256 (sama untuk keduanya — kembalikan raw hex; hanya Lookup API yang menambahkan prefix hmac-sha256=):
// Go
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(stringToSign))
signature := hex.EncodeToString(mac.Sum(nil))
# Python
import hmac, hashlib
sig = hmac.new(secret.encode(), string_to_sign.encode(), hashlib.sha256)
signature = sig.hexdigest()
// Node.js
const crypto = require('crypto');
const signature = crypto.createHmac('sha256', secret).update(stringToSign).digest('hex');
2.4 Validasi yang Dilakukan Server
Server melakukan 5 pengecekan sebelum memproses request:
- Key ID (
X-API-Key-IDatauX-PSP-Key-ID) valid dan terdaftar Timestampfreshness — ditolak jika|now - timestamp| > 300 detik(cegah replay attack)- HMAC signature cocok (constant-time comparison)
- IP caller ada di allowlist (jika dikonfigurasi)
- Rate limit per API Key ID
Jika salah satu gagal: server mengembalikan 401 atau 403 tanpa detail spesifik.
2.5 API Key Rotation
- Rotasi setiap 90 hari — jadwal disepakati saat onboarding
- Grace period 7 hari — dua key aktif bersamaan saat rotasi
- Revokasi instan via tim Kesles — semua request dengan key lama langsung 401
- Secret dikirim ulang via saluran aman (1Password / Bitwarden), tidak pernah via email
3. Base URL
Production : https://api-merchant.kesles.com
Staging : https://api-merchant-staging.kesles.com
4. Endpoint yang Tersedia
4.1 Merchant Lookup (Lookup API — dashboard_api)
| Method | Path | Deskripsi |
|---|---|---|
GET | /api/psp/v1/merchants | List semua merchant aktif (cursor paginated) |
GET | /api/psp/v1/merchants/{id} | Detail merchant by UUID |
GET | /api/psp/v1/merchants/by-nmid/{nmid} | Lookup merchant by NMID (hot endpoint) |
GET | /api/psp/v1/merchants/search | Cari merchant by phone/email/npwp |
4.2 Payment Events (Events API — payment-service)
| Method | Path | Deskripsi |
|---|---|---|
POST | /psp/v1/events | Forward payment event (transaction, refund, nmid-status-changed) |
POST | /psp/v1/settlements | Forward batch settlement harian |
Endpoint berikut dibatasi untuk PSP Internal Tier-1 dan akan mengembalikan 403 untuk PSP bank eksternal:
PATCH /api/psp/v1/merchants/{id}/nmid-assignment
Respons Error
| Code | Arti |
|---|---|
400 | Body request malformed atau nilai field tidak valid |
401 | Header autentikasi hilang atau tidak valid |
403 | Key valid tapi tidak diizinkan untuk endpoint ini |
429 | Rate limit terlampaui — terapkan exponential backoff |
500 | Kegagalan infrastruktur sementara (DB timeout, service unavailable). PSP WAJIB retry dengan exponential backoff untuk respons 500. |
external_event_id yang duplikat bukan 409 (atau error apa pun). Server bersifat idempoten: server membalas HTTP 200 dengan body {"status":"duplicate_skipped"} (opsional disertai field "message"). Perlakukan ini sebagai event yang sudah berhasil diproses — aman untuk diabaikan.
5. Rate Limits
| Endpoint | Limit |
|---|---|
GET /merchants | 60 req/menit per API key |
GET /merchants/{id} | 600 req/menit per API key |
GET /merchants/by-nmid/{nmid} | 1.000 req/menit per API key |
GET /merchants/search | 120 req/menit per API key |
POST /psp/v1/events | 6.000 req/menit per API key |
POST /psp/v1/settlements | 600 req/menit per API key |
Respons 429 jika melebihi limit. Implementasikan exponential backoff.
6. Format Error Standard
{
"error": "human readable message",
"code": "machine_readable_code"
}
Lihat Error Codes untuk daftar lengkap.