import { createHmac } from "node:crypto"; const PORT = process.env.PORT || "3100"; const BASE = process.env.BASE_URL || `http://127.0.0.1:${PORT}`; const ADMIN_TOKEN = process.env.ADMIN_TOKEN || "admin-dev-token"; const DEVICE_TOKEN = process.env.DEVICE_TOKEN || "device-dev-token"; const SECRET = process.env.INTEGRATION_WEBHOOK_SECRET || "dev-callback-secret"; function short(data) { const json = typeof data === 'string' ? data : JSON.stringify(data || {}); return json.length > 180 ? `${json.slice(0, 180)}...` : json; } async function req(path, options = {}) { const response = await fetch(`${BASE}${path}`, { method: options.method || 'GET', headers: { 'Content-Type': 'application/json', ...(options.headers || {}) }, body: Object.prototype.hasOwnProperty.call(options, 'body') ? JSON.stringify(options.body) : undefined }); const text = await response.text(); let body = null; try { body = text ? JSON.parse(text) : null; } catch { body = text; } if (!response.ok) { throw new Error(`${options._label || path} failed: ${response.status}`); } console.log(`${options._label || `${options.method || 'GET'} ${path}`} => ${response.status} ${short(body)}`); return body; } async function reqExpect(path, expectedStatus, options = {}) { const response = await fetch(`${BASE}${path}`, { method: options.method || 'GET', headers: { 'Content-Type': 'application/json', ...(options.headers || {}) }, body: Object.prototype.hasOwnProperty.call(options, 'body') ? JSON.stringify(options.body) : undefined }); const text = await response.text(); let body = null; try { body = text ? JSON.parse(text) : null; } catch { body = text; } if (response.status !== expectedStatus) { throw new Error(`${options._label || path} expected ${expectedStatus}, got ${response.status}`); } console.log(`${options._label || `${options.method || 'GET'} ${path}`} => ${response.status} ${short(body)}`); return body; } async function reqAdmin(path, opts = {}) { return req(path, { ...opts, headers: { ...(opts.headers || {}), Authorization: `Bearer ${ADMIN_TOKEN}` } }); } async function reqDevice(path, opts = {}) { return req(path, { ...opts, headers: { ...(opts.headers || {}), Authorization: `Bearer ${DEVICE_TOKEN}` } }); } (async () => { await req('/health', { _label: 'GET /health' }); await req('/admin/login', { method: 'POST', body: { username: 'admin', password: 'admin' }, _label: 'POST /admin/login' }); await reqAdmin('/admin/seed/status', { _label: 'GET /admin/seed/status' }); const ts = Date.now(); const merchant = await reqAdmin('/admin/merchants', { method: 'POST', body: { legal_name: `Smoke Merchant ${ts}`, brand_name: `SMK-${ts}`, settlement_account_reference: `bank:${ts}`, settlement_account_type: 'merchant_bank_account', payout_mode: 'merchant_direct' }, _label: 'POST /admin/merchants' }); const merchantId = merchant?.data?.id; const outlet = await reqAdmin(`/admin/merchants/${merchantId}/outlets`, { method: 'POST', body: { name: `Outlet ${ts}` }, _label: 'POST /admin/merchants/:id/outlets' }); const outletId = outlet?.data?.id; const terminal = await reqAdmin(`/admin/outlets/${outletId}/terminals`, { method: 'POST', body: { terminal_code: `TERM-${ts}`, qr_mode: 'static' }, _label: 'POST /admin/outlets/:id/terminals' }); const terminalId = terminal?.data?.id; const device = await reqAdmin('/admin/devices', { method: 'POST', body: { device_code: `DEV-${ts}`, vendor: 'acme', model: 'v1', communication_mode: 'mqtt', status: 'active' }, _label: 'POST /admin/devices' }); const deviceId = device?.data?.id; await reqAdmin(`/admin/devices/${deviceId}/bind`, { method: 'POST', body: { merchant_id: merchantId, outlet_id: outletId, terminal_id: terminalId }, _label: 'POST /admin/devices/:id/bind' }); await reqDevice('/device/heartbeat', { method: 'POST', body: { device_id: deviceId, timestamp: new Date().toISOString(), firmware_version: '1.2.3', network_strength: 88, battery_level: 77, state: 'idle' }, _label: 'POST /device/heartbeat' }); const tx = await reqAdmin('/admin/transactions', { method: 'POST', body: { partner_reference: `PR-${ts}`, merchant_id: merchantId, outlet_id: outletId, terminal_id: terminalId, device_id: deviceId, amount: 19900, currency: 'IDR', qr_mode: 'static', initiation_mode: 'static', status: 'initiated' }, _label: 'POST /admin/transactions' }); const txId = tx?.data?.id; const callback = { partner_reference: `PR-${ts}`, partner_txn_id: `PTX-${ts}`, amount: 19900, currency: 'IDR', payment_status: 'paid', status: 'paid', paid_at: new Date().toISOString() }; const signature = createHmac('sha256', SECRET).update(JSON.stringify(callback)).digest('hex'); await req('/integrations/qris/callback', { method: 'POST', headers: { 'X-Partner-Signature': signature }, body: { ...callback, signature }, _label: 'POST /integrations/qris/callback' }); await req('/integrations/qris/callback', { method: 'POST', headers: { 'X-Partner-Signature': signature }, body: { ...callback, signature }, _label: 'POST /integrations/qris/callback duplicate' }); await reqExpect('/integrations/qris/callback', 401, { method: 'POST', headers: { 'X-Partner-Signature': 'bad-signature' }, body: { ...callback, signature: 'bad-signature' }, _label: 'POST /integrations/qris/callback invalid signature' }); await reqAdmin(`/admin/transactions/${txId}`, { _label: 'GET /admin/transactions/:id' }); await reqAdmin(`/admin/transactions/${txId}/events`, { _label: 'GET /admin/transactions/:id/events' }); await reqAdmin(`/admin/ledger-entries?transaction_id=${txId}`, { _label: 'GET /admin/ledger-entries' }); await reqAdmin(`/admin/audit-logs?entity_id=${txId}`, { _label: 'GET /admin/audit-logs' }); await reqAdmin(`/admin/transactions/${txId}/heartbeats`, { _label: 'GET /admin/transactions/:id/heartbeats' }); await reqAdmin(`/admin/devices/${deviceId}/heartbeats`, { _label: 'GET /admin/devices/:id/heartbeats' }); await reqAdmin(`/admin/devices/${deviceId}`, { _label: 'GET /admin/devices/:id health summary' }); await reqAdmin('/admin/notifications/failed', { _label: 'GET /admin/notifications/failed' }); await reqAdmin(`/admin/transactions/${txId}/retry-notification`, { method: 'POST', body: {}, _label: 'POST /admin/transactions/:id/retry-notification' }); await reqAdmin('/admin/dashboard/summary', { _label: 'GET /admin/dashboard/summary' }); const noBindingOutlet = await reqAdmin(`/admin/merchants/${merchantId}/outlets`, { method: 'POST', body: { name: `No Binding Outlet ${ts}` }, _label: 'POST /admin/merchants/:id/outlets no-binding' }); const noBindingOutletId = noBindingOutlet?.data?.id; const noBindingTerminal = await reqAdmin(`/admin/outlets/${noBindingOutletId}/terminals`, { method: 'POST', body: { terminal_code: `TERM-NB-${ts}`, qr_mode: 'static' }, _label: 'POST /admin/outlets/:id/terminals no-binding' }); const noBindingTerminalId = noBindingTerminal?.data?.id; const noBindingTx = await reqAdmin('/admin/transactions', { method: 'POST', body: { partner_reference: `PR-NB-${ts}`, merchant_id: merchantId, outlet_id: noBindingOutletId, terminal_id: noBindingTerminalId, amount: 9900, currency: 'IDR', qr_mode: 'static', initiation_mode: 'static', status: 'initiated' }, _label: 'POST /admin/transactions no-binding' }); const noBindingCallback = { partner_reference: `PR-NB-${ts}`, partner_txn_id: `PTX-NB-${ts}`, amount: 9900, currency: 'IDR', payment_status: 'paid', status: 'paid', paid_at: new Date().toISOString() }; const noBindingSignature = createHmac('sha256', SECRET).update(JSON.stringify(noBindingCallback)).digest('hex'); await req('/integrations/qris/callback', { method: 'POST', headers: { 'X-Partner-Signature': noBindingSignature }, body: { ...noBindingCallback, signature: noBindingSignature }, _label: 'POST /integrations/qris/callback no-binding' }); await reqAdmin(`/admin/ledger-entries?transaction_id=${noBindingTx?.data?.id}`, { _label: 'GET /admin/ledger-entries no-binding' }); await reqAdmin('/admin/notifications/failed', { _label: 'GET /admin/notifications/failed no-binding' }); const dynamicOutlet = await reqAdmin(`/admin/merchants/${merchantId}/outlets`, { method: 'POST', body: { name: `Dynamic API Outlet ${ts}` }, _label: 'POST /admin/merchants/:id/outlets dynamic-api' }); const dynamicOutletId = dynamicOutlet?.data?.id; const dynamicTerminal = await reqAdmin(`/admin/outlets/${dynamicOutletId}/terminals`, { method: 'POST', body: { terminal_code: `TERM-DYN-${ts}`, qr_mode: 'dynamic_api' }, _label: 'POST /admin/outlets/:id/terminals dynamic-api' }); const dynamicTerminalId = dynamicTerminal?.data?.id; const dynamicDevice = await reqAdmin('/admin/devices', { method: 'POST', body: { device_code: `DEV-API-${ts}`, vendor: 'acme', model: 'api-v1', communication_mode: 'api', capability_profile_json: { dynamic_qr: { api_direct: true, mqtt: false }, flows: ['dynamic_qr:api_direct', 'static_payment_notification'] }, status: 'active' }, _label: 'POST /admin/devices dynamic-api' }); const dynamicDeviceId = dynamicDevice?.data?.id; await reqAdmin(`/admin/devices/${dynamicDeviceId}/bind`, { method: 'POST', body: { merchant_id: merchantId, outlet_id: dynamicOutletId, terminal_id: dynamicTerminalId }, _label: 'POST /admin/devices/:id/bind dynamic-api' }); await reqExpect('/device/transactions/dynamic-qr', 400, { method: 'POST', headers: { Authorization: `Bearer ${DEVICE_TOKEN}` }, body: { device_id: deviceId, terminal_id: dynamicTerminalId, amount: 15000, currency: 'IDR', request_id: `DYN-STATIC-${ts}` }, _label: 'POST /device/transactions/dynamic-qr unsupported device' }); const dynamicRequestId = `DYN-REQ-${ts}`; const dynamicQr = await reqDevice('/device/transactions/dynamic-qr', { method: 'POST', headers: { 'Idempotency-Key': dynamicRequestId }, body: { device_id: dynamicDeviceId, terminal_id: dynamicTerminalId, amount: 32100, currency: 'IDR', request_id: dynamicRequestId }, _label: 'POST /device/transactions/dynamic-qr' }); const dynamicQrReplay = await reqDevice('/device/transactions/dynamic-qr', { method: 'POST', headers: { 'Idempotency-Key': dynamicRequestId }, body: { device_id: dynamicDeviceId, terminal_id: dynamicTerminalId, amount: 32100, currency: 'IDR', request_id: dynamicRequestId }, _label: 'POST /device/transactions/dynamic-qr duplicate' }); if (dynamicQr?.data?.transaction_id !== dynamicQrReplay?.data?.transaction_id) { throw new Error('dynamic QR idempotency returned a different transaction'); } const dynamicCallback = { partner_reference: dynamicQr?.data?.partner_reference, partner_txn_id: `PTX-DYN-${ts}`, amount: 32100, currency: 'IDR', payment_status: 'paid', status: 'paid', paid_at: new Date().toISOString() }; const dynamicSignature = createHmac('sha256', SECRET).update(JSON.stringify(dynamicCallback)).digest('hex'); await req('/integrations/qris/callback', { method: 'POST', headers: { 'X-Partner-Signature': dynamicSignature }, body: { ...dynamicCallback, signature: dynamicSignature }, _label: 'POST /integrations/qris/callback dynamic-api' }); await reqAdmin(`/admin/transactions/${dynamicQr?.data?.transaction_id}`, { _label: 'GET /admin/transactions/:id dynamic-api' }); const dueDynamicTx = await reqAdmin('/admin/transactions', { method: 'POST', body: { partner_reference: `DUE-DYN-${ts}`, merchant_id: merchantId, outlet_id: dynamicOutletId, terminal_id: dynamicTerminalId, device_id: dynamicDeviceId, amount: 12000, currency: 'IDR', qr_mode: 'dynamic', initiation_mode: 'dynamic_api', status: 'awaiting_payment', expired_at: new Date(Date.now() - 60_000).toISOString() }, _label: 'POST /admin/transactions due dynamic' }); await reqAdmin('/admin/transactions/expire-due', { method: 'POST', body: { limit: 10 }, _label: 'POST /admin/transactions/expire-due' }); await reqAdmin(`/admin/transactions/${dueDynamicTx?.data?.id}`, { _label: 'GET /admin/transactions/:id expired dynamic' }); const mqttOutlet = await reqAdmin(`/admin/merchants/${merchantId}/outlets`, { method: 'POST', body: { name: `Dynamic MQTT Outlet ${ts}` }, _label: 'POST /admin/merchants/:id/outlets dynamic-mqtt' }); const mqttOutletId = mqttOutlet?.data?.id; const mqttTerminal = await reqAdmin(`/admin/outlets/${mqttOutletId}/terminals`, { method: 'POST', body: { terminal_code: `TERM-MQTT-${ts}`, qr_mode: 'dynamic_mqtt' }, _label: 'POST /admin/outlets/:id/terminals dynamic-mqtt' }); const mqttTerminalId = mqttTerminal?.data?.id; const mqttDevice = await reqAdmin('/admin/devices', { method: 'POST', body: { device_code: `DEV-MQTT-${ts}`, vendor: 'acme', model: 'mqtt-v1', communication_mode: 'mqtt', capability_profile_json: { dynamic_qr: { api_direct: false, mqtt: true }, flows: ['dynamic_qr:mqtt', 'static_payment_notification'] }, status: 'active' }, _label: 'POST /admin/devices dynamic-mqtt' }); const mqttDeviceId = mqttDevice?.data?.id; await reqAdmin(`/admin/devices/${mqttDeviceId}/bind`, { method: 'POST', body: { merchant_id: merchantId, outlet_id: mqttOutletId, terminal_id: mqttTerminalId }, _label: 'POST /admin/devices/:id/bind dynamic-mqtt' }); const mqttRequestId = `MQTT-DYN-${ts}`; const mqttQr = await reqDevice('/device/mqtt/uplink/dynamic-qr/request', { method: 'POST', body: { message_type: 'dynamic_qr_request', request_id: mqttRequestId, device_id: mqttDeviceId, terminal_id: mqttTerminalId, amount: 43200, currency: 'IDR', created_at: new Date().toISOString() }, _label: 'POST /device/mqtt/uplink/dynamic-qr/request' }); const mqttQrReplay = await reqDevice('/device/mqtt/uplink/dynamic-qr/request', { method: 'POST', body: { message_type: 'dynamic_qr_request', request_id: mqttRequestId, device_id: mqttDeviceId, terminal_id: mqttTerminalId, amount: 43200, currency: 'IDR', created_at: new Date().toISOString() }, _label: 'POST /device/mqtt/uplink/dynamic-qr/request duplicate' }); if (mqttQr?.data?.transaction_id !== mqttQrReplay?.data?.transaction_id) { throw new Error('MQTT dynamic QR idempotency returned a different transaction'); } await reqAdmin(`/admin/devices/${mqttDeviceId}/mqtt-messages?correlation_id=${mqttRequestId}`, { _label: 'GET /admin/devices/:id/mqtt-messages dynamic-mqtt' }); const mqttCallback = { partner_reference: mqttQr?.data?.partner_reference, partner_txn_id: `PTX-MQTT-${ts}`, amount: 43200, currency: 'IDR', payment_status: 'paid', status: 'paid', paid_at: new Date().toISOString() }; const mqttSignature = createHmac('sha256', SECRET).update(JSON.stringify(mqttCallback)).digest('hex'); await req('/integrations/qris/callback', { method: 'POST', headers: { 'X-Partner-Signature': mqttSignature }, body: { ...mqttCallback, signature: mqttSignature }, _label: 'POST /integrations/qris/callback dynamic-mqtt' }); await reqAdmin(`/admin/transactions/${mqttQr?.data?.transaction_id}`, { _label: 'GET /admin/transactions/:id dynamic-mqtt' }); const pushedConfig = await reqAdmin(`/admin/devices/${dynamicDeviceId}/config`, { method: 'PATCH', body: { settings: { volume: 65, language: 'id-ID', heartbeat_interval_seconds: 45 } }, _label: 'PATCH /admin/devices/:id/config' }); const configVersion = pushedConfig?.data?.config?.config_version; await reqAdmin(`/admin/devices/${dynamicDeviceId}/config/status`, { _label: 'GET /admin/devices/:id/config/status pending' }); await reqAdmin(`/admin/devices/${dynamicDeviceId}/config/retry-push`, { method: 'POST', body: {}, _label: 'POST /admin/devices/:id/config/retry-push' }); await reqDevice(`/device/config?device_id=${dynamicDeviceId}`, { _label: 'GET /device/config' }); await reqDevice('/device/config/ack', { method: 'POST', body: { device_id: dynamicDeviceId, config_version: configVersion, status: 'applied', result_payload: { applied_at: new Date().toISOString() } }, _label: 'POST /device/config/ack' }); await reqAdmin(`/admin/devices/${dynamicDeviceId}/config`, { _label: 'GET /admin/devices/:id/config' }); await reqAdmin(`/admin/devices/${dynamicDeviceId}/config/status`, { _label: 'GET /admin/devices/:id/config/status applied' }); await reqExpect(`/admin/devices/${dynamicDeviceId}/config/retry-push`, 409, { method: 'POST', headers: { Authorization: `Bearer ${ADMIN_TOKEN}` }, body: {}, _label: 'POST /admin/devices/:id/config/retry-push already applied' }); await reqAdmin(`/admin/devices/${dynamicDeviceId}/mqtt-messages?message_type=config_push`, { _label: 'GET /admin/devices/:id/mqtt-messages config' }); await reqAdmin(`/admin/devices/${dynamicDeviceId}/mqtt-messages?message_type=config_ack`, { _label: 'GET /admin/devices/:id/mqtt-messages config ack' }); console.log(`Smoke point 4 flow done. tx=${txId} device=${deviceId}`); })();