Files
presensi/app/Modules/Academic/Services/DapodikClient.php
mwpn 8d7cdd05b7 fix: dapodik client token & error detail
Kirim token juga via query param untuk kompatibilitas WebService Dapodik.
Tambahkan detil error (HTTP code + snippet) saat response bukan JSON/empty.
2026-03-06 16:29:48 +07:00

153 lines
5.2 KiB
PHP

<?php
namespace App\Modules\Academic\Services;
/**
* Dapodik WebService API client.
* Uses env: DAPODIK_BASE_URL, DAPODIK_TOKEN, DAPODIK_NPSN.
* Do not log or expose token.
*/
class DapodikClient
{
protected string $baseUrl;
protected string $token;
protected string $npsn;
public function __construct(?string $baseUrl = null, ?string $token = null, ?string $npsn = null)
{
$this->baseUrl = rtrim($baseUrl ?? (string) env('DAPODIK_BASE_URL', ''), '/');
$this->token = $token ?? (string) env('DAPODIK_TOKEN', '');
$this->npsn = $npsn ?? (string) env('DAPODIK_NPSN', '');
}
/**
* GET getSekolah
*
* @return array{success: bool, data?: array, error?: string}
*/
public function getSekolah(): array
{
$url = $this->baseUrl . '/getSekolah';
if ($this->npsn !== '') {
$url .= '?npsn=' . rawurlencode($this->npsn);
}
return $this->request('GET', $url);
}
/**
* GET getPesertaDidik with pagination
*
* @param int $start
* @param int $limit
* @return array{success: bool, data?: array, rows?: array, id?: mixed, start?: int, limit?: int, results?: int, error?: string}
*/
public function getPesertaDidik(int $start = 0, int $limit = 200): array
{
$params = [];
if ($this->npsn !== '') {
$params['npsn'] = $this->npsn;
}
$params['start'] = $start;
$params['limit'] = $limit;
$url = $this->baseUrl . '/getPesertaDidik?' . http_build_query($params);
return $this->request('GET', $url);
}
/**
* Execute HTTP request. Returns decoded JSON with success flag and error message on failure.
*/
protected function request(string $method, string $url): array
{
// Dapodik WebService commonly expects `token` as query param.
// We still send Authorization header for compatibility.
if ($this->token !== '' && strpos($url, 'token=') === false) {
$url = $this->appendQueryParam($url, 'token', $this->token);
}
$ch = curl_init();
if ($ch === false) {
return ['success' => false, 'error' => 'cURL init failed'];
}
$headers = ['Accept: application/json'];
if ($this->token !== '') {
$headers[] = 'Authorization: Bearer ' . $this->token;
}
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_CUSTOMREQUEST => $method,
]);
$body = curl_exec($ch);
$errno = curl_errno($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = (string) (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?? '');
curl_close($ch);
if ($errno) {
return ['success' => false, 'error' => 'Network error: ' . ($errno === CURLE_OPERATION_TIMEDOUT ? 'timeout' : 'curl ' . $errno)];
}
$bodyStr = is_string($body) ? $body : '';
if (trim($bodyStr) === '') {
return ['success' => false, 'error' => 'Empty response from Dapodik', 'http_code' => $httpCode, 'content_type' => $contentType];
}
$decoded = json_decode($bodyStr, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$snippet = substr(trim(strip_tags($bodyStr)), 0, 200);
$extra = $snippet !== '' ? ' — ' . $snippet : '';
$codePart = $httpCode > 0 ? ' (HTTP ' . $httpCode . ')' : '';
return ['success' => false, 'error' => 'Invalid JSON response' . $codePart . $extra, 'http_code' => $httpCode, 'content_type' => $contentType];
}
if ($httpCode < 200 || $httpCode >= 300) {
$msg = is_array($decoded) && isset($decoded['message']) ? $decoded['message'] : 'HTTP ' . $httpCode;
return ['success' => false, 'error' => $msg, 'http_code' => $httpCode, 'data' => $decoded];
}
return array_merge(['success' => true], $decoded);
}
/**
* Append a query param to URL, preserving fragment.
*/
private function appendQueryParam(string $url, string $key, string $value): string
{
$frag = '';
$base = $url;
$hashPos = strpos($url, '#');
if ($hashPos !== false) {
$base = substr($url, 0, $hashPos);
$frag = substr($url, $hashPos);
}
$sep = (strpos($base, '?') === false) ? '?' : '&';
return $base . $sep . rawurlencode($key) . '=' . rawurlencode($value) . $frag;
}
/**
* Normalize Dapodik response to a list of rows.
* Dapodik returns { results, id, start, limit, rows: [...] }
*
* @param array $response Response from getPesertaDidik
* @return array<int, array<string, mixed>>
*/
public static function normalizePesertaDidikRows(array $response): array
{
if (! isset($response['rows']) || ! is_array($response['rows'])) {
return [];
}
$rows = $response['rows'];
$out = [];
foreach ($rows as $i => $row) {
$out[] = is_array($row) ? $row : (array) $row;
}
return $out;
}
}