# Integrasi Nobu QRIS (Ringkasan & Cara Penggunaan)

Dokumen ini menjelaskan variabel environment, format tanda tangan (signature), contoh permintaan (cURL), dan contoh penggunaan di Laravel untuk endpoint Nobu QRIS yang ada di project.

**Lokasi implementasi di repo**
- Controller utama: [app/Http/Controllers/QrisController.php](app/Http/Controllers/QrisController.php#L1-L120)
- Helper tanda tangan: [app/Helpers/SignatureHelper.php](app/Helpers/SignatureHelper.php#L1-L120)
- Dokumentasi API ter-generate: lihat folder `.scribe` (mis. `/.scribe/endpoints/11.yaml`)

**Environment (pada .env)**
- `NOBU_SIGNATURE_SECRET_KEY` : secret/key yang digunakan untuk HMAC (diberikan oleh Nobu sandbox/production).
- `NOBU_QRIS_MERCHANT_ID` : merchant id yang diberikan.
- `NOBU_QRIS_SUB_MERCHANT_ID` : sub-merchant id.
- `NOBU_QRIS_STORE_ID` : store id (kadang disebut externalStoreId / storeId).
- `NOBU_QRIS_GENERATE_URL` : endpoint generate QRIS (misal sandbox).
- `NOBU_QRIS_CEK_STATUS_URL` : endpoint check status QRIS.
- `NOBU_QRIS_EXPIRED_MINUTES` : lama kadaluarsa token/QR lokal (menit).
- `NOBU_QRIS_MIN_AMOUNT` / `NOBU_QRIS_MAX_AMOUNT` : batas nominal.

Contoh di `.env` (sudah ada di repo):
NOBU_QRIS_GENERATE_URL=https://api-sandboxv2.clusterkita.com/v1.0/nobu/qr/qr-generate/
NOBU_QRIS_CEK_STATUS_URL=https://api-sandboxv2.clusterkita.com/v1.0/nobu/qr/qr-query/

---

**Algoritme tanda tangan (signature)**

Project menyediakan `SignatureHelper::validateSignatureSymmetric(...)` yang melakukan langkah-langkah berikut (harus diikuti pada setiap request outgoing ke Nobu):

1. Jika token berupa `Bearer <tok>`, hilangkan prefix `Bearer ` untuk string to sign.
2. Hitung SHA256 dari body JSON: `bodySha256 = hash('sha256', bodyJson)`.
3. Bentuk string to sign:

   METHOD:PATH:TOKEN:BODY_SHA256:TIMESTAMP

   Contoh: `POST:/v1.0/nobu/qr/qr-generate/:<token_without_prefix>:<bodySha256>:2024-01-01T12:00:00+07:00`

4. Hitung HMAC-SHA512 dari string tersebut menggunakan `clientSecret` (yaitu `NOBU_SIGNATURE_SECRET_KEY`).
5. Ambil dua bentuk keluaran dari helper:
   - hex (untuk logging/diagnose)
   - base64 (yang dipakai sebagai header `X-SIGNATURE`)

6. Sertakan header:
   - `Authorization: Bearer <your-internal-token>`
   - `X-TIMESTAMP: <RFC3339 timestamp>` (format `Y-m-d\TH:i:sP` seperti `2024-01-01T12:00:00+07:00`)
   - `X-SIGNATURE: <base64-hmac>`
   - `Content-Type: application/json`

Catatan: `path` yang dipakai adalah path + query pada URL (mis. `/v1.0/nobu/qr/qr-generate/` atau jika ada query append `?a=b`). Gunakan `parse_url()` seperti pada controller.

---

**Contoh PHP (menggunakan helper yang ada)**

```php
use App\Helpers\SignatureHelper;

$nobuUrl = env('NOBU_QRIS_GENERATE_URL');
$payload = [ /* ... */ ];
$payloadJson = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$nobuSecret = env('NOBU_SIGNATURE_SECRET_KEY');
$nobuTimestamp = now()->format('Y-m-d\TH:i:sP');
$parsed = parse_url($nobuUrl ?: '');
$nobuPath = ($parsed['path'] ?? '/') . (isset($parsed['query']) ? '?' . $parsed['query'] : '');
$authHeader = 'Bearer ' . $yourInternalToken;

[ $hex, $base64 ] = SignatureHelper::validateSignatureSymmetric(
    'POST',
    $nobuPath,
    $authHeader,
    $nobuTimestamp,
    $payloadJson,
    $nobuSecret
);

$response = Http::withHeaders([
    'Authorization' => $authHeader,
    'X-TIMESTAMP' => $nobuTimestamp,
    'X-SIGNATURE' => $base64,
    'Content-Type' => 'application/json'
])->post($nobuUrl, $payload);
```

---

**Contoh membuat signature manual (PHP) — setara dengan helper**

```php
$method = 'POST';
$parsed = parse_url($nobuUrl);
$path = ($parsed['path'] ?? '/') . (isset($parsed['query']) ? '?' . $parsed['query'] : '');
$token = $yourInternalToken; // tanpa prefix "Bearer " saat dimasukkan ke string to sign
$bodyJson = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$bodySha256 = hash('sha256', $bodyJson);
$stringToSign = "$method:$path:$token:$bodySha256:$nobuTimestamp";
$hmacRaw = hash_hmac('sha512', $stringToSign, $nobuSecret, true);
$signatureBase64 = base64_encode($hmacRaw);

// pakai header X-SIGNATURE: $signatureBase64
```

---

**Contoh cURL (generate)**

1) Hitung `X-TIMESTAMP` dan `X-SIGNATURE` seperti contoh di atas.

```bash
curl -X POST "${NOBU_QRIS_GENERATE_URL}" \
  -H "Authorization: Bearer YOUR_INTERNAL_TOKEN" \
  -H "X-TIMESTAMP: 2024-01-01T12:00:00+07:00" \
  -H "X-SIGNATURE: <BASE64_SIGNATURE>" \
  -H "Content-Type: application/json" \
  -d '{
    "partnerReferenceNo":"REF123",
    "amount":{"value":"10000","currency":"IDR"},
    "merchantId":"<MERCHANT_ID>",
    "subMerchantId":"<SUB_ID>",
    "storeId":"<STORE_ID>"
  }'
```

**Contoh cURL (cek status)**

```bash
curl -X POST "${NOBU_QRIS_CEK_STATUS_URL}" \
  -H "Authorization: Bearer YOUR_INTERNAL_TOKEN" \
  -H "X-TIMESTAMP: 2024-01-01T12:05:00+07:00" \
  -H "X-SIGNATURE: <BASE64_SIGNATURE>" \
  -H "Content-Type: application/json" \
  -d '{
    "originalPartnerReferenceNo":"REF123",
    "merchantId":"<MERCHANT_ID>",
    "subMerchantId":"<SUB_ID>",
    "externalStoreId":"<STORE_ID>"
  }'
```

---

**Endpoint internal di project (untuk referensi penggunaan)**
- `POST /api/qris/generate` — memanggil Nobu `qr-generate` (lihat [app/Http/Controllers/QrisController.php](app/Http/Controllers/QrisController.php#L1-L120))
- `POST /api/qris/check-status` — memanggil Nobu `qr-query` (lihat [app/Http/Controllers/QrisController.php](app/Http/Controllers/QrisController.php#L120-L260))

Gunakan endpoint API internal ini bila ingin memanfaatkan mekanisme signature dan penyimpanan yang sudah ada di project.

---

**Tip Debugging**
- Pastikan `X-TIMESTAMP` masih dalam rentang waktu yang diterima oleh Nobu (sinkronkan timezone server).
- Pastikan `path` yang dipakai saat sign persis sama dengan path pada URL (perhatikan trailing slash dan query string).
- Pastikan token yang dimasukkan ke string-to-sign tidak mengandung prefix `Bearer ` (helper sudah menangani ini).
- Untuk logging: gunakan bentuk hex yang dikembalikan helper untuk membandingkan HMAC tanpa base64.
- Periksa response body Nobu untuk `responseCode` / `responseMessage` jika terjadi kegagalan.

---