Skip to main content

Partner Auth API — Dapatkan JWT Context dengan HMAC Signature

POST /api/v1/auth/context

Integrasi dengan Partner API TuplePay terdiri dari dua langkah. Langkah pertama adalah menandatangani request menggunakan HMAC signature dengan Partner Credential yang disediakan oleh TuplePay, kemudian menggunakan signature tersebut untuk mendapatkan JWT context dengan masa berlaku singkat untuk tipe pengguna MERCHANT. Body request boleh kosong karena seluruh proses autentikasi dilakukan melalui enam header yang dijelaskan di bawah.

Langkah kedua dilakukan saat mengakses layanan transaksi: gunakan JWT yang diperoleh sebagai header Authorization: Bearer <token> untuk memanggil endpoint pada Partner Transactions API, seperti pembuatan QRIS dan disbursement.

Simpan dan gunakan kembali JWT tersebut selama masih berlaku. Hindari memanggil endpoint autentikasi ini untuk setiap request transaksi. Pola ini serupa dengan alur client_credentials pada OAuth 2.0.

Header yang wajib dikirim

Keenam header ini wajib dikirim secara lengkap. Jika salah satu header tidak diisi, request akan ditolak dengan 400 Bad Request (MISSING_HEADER) sebelum diproses lebih lanjut.

HeaderDeskripsi
X-Partner-IdUUID Partner (lihat dashboard Partner).
X-Key-VersionVersi Partner Credential yang digunakan untuk menandatangani request ini. Satu Partner dapat memiliki beberapa versi credential yang aktif secara bersamaan, misalnya selama proses rotasi key.
X-EnvironmentSANDBOX atau PRODUCTION — nilainya harus sesuai dengan environment Partner Credential yang digunakan.
X-TimestampEpoch seconds saat request dibuat. Request akan ditolak jika timestamp berada di luar toleransi 300 detik (5 menit) dari waktu server. Mekanisme ini membantu mencegah replay attack menggunakan request lama.
X-NonceString unik untuk setiap request, dengan UUID v4 sebagai format yang direkomendasikan. Digunakan bersama X-Timestamp untuk mencegah replay attack. Kombinasi nonce dan Partner ID yang sama akan ditolak.
X-SignatureBase64 hasil HMAC-SHA256 atau RSA-SHA256, tergantung pada tipe kunci Partner Credential yang digunakan, atas string-to-sign yang dijelaskan di bawah.

Membentuk String-to-Sign

Signature dihitung dari string yang terdiri dari lima bagian, dengan setiap bagian dipisahkan oleh karakter baris baru (\n):

Format string-to-sign
{method}
{path}
{timestamp}
{nonce}
{sha256hex(body)}
  • method -- method HTTP dalam huruf besar, misalnya POST.
  • path -- path request, misalnya /api/v1/auth/context (tanpa query string).
  • timestamp -- nilai yang sama persis dengan header X-Timestamp (epoch seconds).
  • nonce -- nilai yang sama persis dengan header X-Nonce.
  • sha256hex(body) -- SHA-256 dari body request dalam bentuk hex string. Kalau body kosong, ini adalah SHA-256 dari string kosong.

Contoh request

Request
TIMESTAMP=$(date +%s)
NONCE=$(uuidgen)
BODY_SHA256=$(printf '' | openssl dgst -sha256 | cut -d' ' -f2)

STRING_TO_SIGN="POST
/api/v1/auth/context
${TIMESTAMP}
${NONCE}
${BODY_SHA256}"

SIGNATURE=$(printf '%s' "$STRING_TO_SIGN" \
| openssl dgst -sha256 -hmac "$TUPLEPAY_HMAC_SECRET" -binary \
| base64)

curl -X POST '{{BASE_URL}}/api/v1/auth/context' \
-H "X-Partner-Id: $TUPLEPAY_PARTNER_ID" \
-H "X-Key-Version: 1" \
-H "X-Environment: SANDBOX" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $NONCE" \
-H "X-Signature: $SIGNATURE"
note

{{BASE_URL}} — ganti dengan base URL environment TuplePay yang digunakan (SANDBOX atau PRODUCTION) sesuai yang diberikan oleh tim integrasi partner. Contoh di atas menggunakan HMAC-SHA256. Jika Partner Credential menggunakan tipe RSA, proses pembentukan stringToSign tetap sama; yang berbeda hanya proses penandatanganannya, yaitu menggunakan private key RSA alih-alih createHmac.

Response sukses

200 OK -- signature valid, JWT context berhasil diterbitkan.

200 OK
{
"code": "00",
"message": "Success",
"data": {
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtZXJjaGFudCJ9...",
"tokenType": "Bearer",
"partnerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"credentialId": "b2c3d4e5-6f78-90ab-cdef-1234567890ab"
},
"correlationId": "9f8e7d6c-5b4a-3210-fedc-ba9876543210",
"requestId": "1a2b3c4d-5e6f-7890-abcd-ef1234567890",
"timestamp": "2026-08-13T09:15:30.123Z"
}

Pakai data.accessToken sebagai header Authorization: Bearer <accessToken> untuk memanggil Partner Transactions API -- misalnya buat transaksi QRIS.

Response ini tidak menyertakan field masa berlaku terpisah seperti expiresIn. accessToken merupakan JWT standar; decode token dan gunakan klaim exp untuk mengetahui waktu kedaluwarsanya.

Response error

Body error hanya berisi dua field: code dan message. Endpoint ini gagal sebelum request masuk ke lapisan yang menambahkan correlationId, requestId, dan timestamp (berbeda dari response endpoint lain di dokumentasi ini; lihat catatan di bawah tabel).

Untuk 9 kode kegagalan verifikasi (PARTNER_NOT_FOUND hingga IP_NOT_WHITELISTED pada tabel di bawah), nilai message sengaja digeneralisasi menjadi satu kalimat yang sama. Tujuannya untuk mencegah informasi mengenai bagian kredensial yang tidak valid terbuka kepada pemanggil yang belum terverifikasi sebagai partner.

MISSING_HEADER merupakan pengecualian: pesannya menyebutkan header yang tidak diisi karena pada tahap ini identitas pemanggil belum diverifikasi dan tidak ada informasi sensitif yang dapat dibocorkan melalui nama header.

CONTEXT_TOKEN_MINT_FAILED juga merupakan pengecualian — lihat catatan di bawah tabel.

Contoh -- 401 Unauthorized (9 kode verifikasi)
{
"code": "INVALID_SIGNATURE",
"message": "Otentikasi partner ditolak"
}
Contoh -- 400 Bad Request (header hilang)
{
"code": "MISSING_HEADER",
"message": "Header 'X-Timestamp' wajib diisi"
}
HTTP StatuscodeKapan terjadi
400MISSING_HEADERSalah satu dari 6 header wajib tidak dikirim atau formatnya tidak valid.
401PARTNER_NOT_FOUNDX-Partner-Id tidak terdaftar.
401PARTNER_NOT_ACTIVEPartner ditemukan tapi statusnya bukan ACTIVE.
401CREDENTIAL_NOT_FOUNDKombinasi Partner + X-Key-Version + X-Environment tidak ditemukan.
401CREDENTIAL_NOT_ACTIVECredential ditemukan tapi statusnya bukan ACTIVE.
401CREDENTIAL_EXPIREDCredential sudah lewat masa berlaku.
401INVALID_SIGNATUREX-Signature tidak cocok dengan string-to-sign yang dihitung server.
401TIMESTAMP_EXPIREDX-Timestamp di luar batas toleransi (default 300 detik).
401REPLAY_DETECTEDKombinasi X-Nonce + X-Partner-Id sudah pernah dipakai.
403IP_NOT_WHITELISTEDAlamat IP pemanggil tidak ada di IP whitelist Partner.
503CONTEXT_TOKEN_MINT_FAILEDKredensial kamu valid, tapi backend TuplePay gagal menerbitkan JWT context (gangguan sistem sementara). Aman langsung di-retry -- tidak ada yang salah dengan signature/kredensial kamu.
Contoh -- 503 Service Unavailable
{
"code": "CONTEXT_TOKEN_MINT_FAILED",
"message": "Gagal memproses otentikasi karena gangguan sistem sementara, coba lagi."
}
note

CONTEXT_TOKEN_MINT_FAILED terjadi setelah kredensial berhasil melalui seluruh proses verifikasi. Berbeda dari 9 kode di atas, pesan error ini sengaja tidak digeneralisasi karena tidak ada risiko kebocoran informasi. Pesan yang spesifik juga membantu mengidentifikasi bahwa kegagalan terjadi karena gangguan sementara pada sisi TuplePay, bukan karena kredensial yang tidak valid.