461 lines
18 KiB
PHP
461 lines
18 KiB
PHP
<?php
|
|
|
|
namespace App\Libraries;
|
|
|
|
use App\Config\ExternalApi;
|
|
|
|
class MekariWhatsAppService
|
|
{
|
|
private $hmacUsername;
|
|
private $hmacSecret;
|
|
private $apiUrl;
|
|
private $channelIntegrationId;
|
|
private $messageTemplateId;
|
|
private $messageTemplateIdPelanggaranID;
|
|
private $messageTemplateIdPelanggaranEN;
|
|
private $uploadApiUrl;
|
|
|
|
public function __construct()
|
|
{
|
|
$config = config('ExternalApi');
|
|
|
|
$this->hmacUsername = $config->hmacUsername;
|
|
$this->hmacSecret = $config->hmacSecret;
|
|
$this->channelIntegrationId = $config->channelIntegrationId;
|
|
$this->messageTemplateId = $config->messageTemplateId;
|
|
$this->messageTemplateIdPelanggaranID = $config->messageTemplateIdPelanggaranID;
|
|
$this->messageTemplateIdPelanggaranEN = $config->messageTemplateIdPelanggaranEN;
|
|
$this->apiUrl = 'https://api.mekari.com/qontak/chat/v1/broadcasts/whatsapp/direct';
|
|
$this->uploadApiUrl = 'https://api.mekari.com/qontak/chat/v1/file_uploader';
|
|
}
|
|
|
|
/**
|
|
* Upload foto ke Mekari untuk digunakan di WhatsApp template
|
|
* Jika file WebP, akan dikonversi ke JPEG dulu
|
|
*/
|
|
public function uploadImage($filePath)
|
|
{
|
|
$tempJpegPath = null;
|
|
|
|
try {
|
|
if (!file_exists($filePath)) {
|
|
throw new \Exception("File tidak ditemukan: {$filePath}");
|
|
}
|
|
|
|
$fileExtension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
|
|
$uploadFilePath = $filePath;
|
|
$uploadFileName = basename($filePath);
|
|
|
|
// Konversi WebP ke JPEG
|
|
if ($fileExtension === 'webp') {
|
|
log_message('debug', "Converting WebP to JPEG: {$filePath}");
|
|
|
|
$tempJpegPath = sys_get_temp_dir() . '/' . uniqid('wa_upload_') . '.jpg';
|
|
$image = imagecreatefromwebp($filePath);
|
|
|
|
if (!$image) {
|
|
throw new \Exception("Gagal membaca file WebP");
|
|
}
|
|
|
|
$converted = imagejpeg($image, $tempJpegPath, 90);
|
|
imagedestroy($image);
|
|
|
|
if (!$converted) {
|
|
throw new \Exception("Gagal konversi WebP ke JPEG");
|
|
}
|
|
|
|
$uploadFilePath = $tempJpegPath;
|
|
$uploadFileName = pathinfo($filePath, PATHINFO_FILENAME) . '.jpg';
|
|
|
|
log_message('debug', "WebP converted to JPEG: {$tempJpegPath}");
|
|
}
|
|
|
|
$urlParts = parse_url($this->uploadApiUrl);
|
|
$path = $urlParts['path'];
|
|
$dateString = gmdate('D, d M Y H:i:s') . ' GMT';
|
|
$hmacAuth = $this->generateHmacAuth('POST', $path, $dateString);
|
|
|
|
$cFile = new \CURLFile($uploadFilePath, 'image/jpeg', $uploadFileName);
|
|
|
|
$ch = curl_init($this->uploadApiUrl);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_POST => true,
|
|
CURLOPT_HTTPHEADER => [
|
|
'Authorization: ' . $hmacAuth,
|
|
'Date: ' . $dateString
|
|
],
|
|
CURLOPT_POSTFIELDS => ['file' => $cFile],
|
|
CURLOPT_TIMEOUT => 60,
|
|
CURLOPT_SSL_VERIFYPEER => true
|
|
]);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$error = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
// Cleanup temporary file
|
|
if ($tempJpegPath && file_exists($tempJpegPath)) {
|
|
unlink($tempJpegPath);
|
|
log_message('debug', "Temporary JPEG file deleted: {$tempJpegPath}");
|
|
}
|
|
|
|
if ($error) {
|
|
log_message('error', "Mekari Upload cURL Error: {$error}");
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Gagal upload gambar ke server'
|
|
];
|
|
}
|
|
|
|
log_message('debug', "Mekari Upload Response [{$httpCode}]: {$response}");
|
|
|
|
$result = json_decode($response, true);
|
|
|
|
if ($httpCode >= 200 && $httpCode < 300 && isset($result['data']['url'])) {
|
|
return [
|
|
'success' => true,
|
|
'message' => 'Gambar berhasil diupload',
|
|
'url' => $result['data']['url'],
|
|
'filename' => $result['data']['filename']
|
|
];
|
|
}
|
|
|
|
$errorMessage = 'Gagal upload gambar';
|
|
if (isset($result['error']['message'])) {
|
|
$errorMessage = $result['error']['message'];
|
|
} elseif (isset($result['message'])) {
|
|
$errorMessage = $result['message'];
|
|
}
|
|
|
|
return [
|
|
'success' => false,
|
|
'message' => $errorMessage
|
|
];
|
|
|
|
} catch (\Exception $e) {
|
|
if ($tempJpegPath && file_exists($tempJpegPath)) {
|
|
unlink($tempJpegPath);
|
|
}
|
|
|
|
log_message('error', "Mekari Upload Exception: " . $e->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Terjadi kesalahan saat upload gambar: ' . $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
|
|
public function sendOtp($phoneNumber, $otpCode, $userName = '', $subject = 'VERIFIKASI AKUN')
|
|
{
|
|
try {
|
|
$phoneNumber = $this->formatPhoneNumber($phoneNumber);
|
|
$urlParts = parse_url($this->apiUrl);
|
|
$path = $urlParts['path'];
|
|
$dateString = gmdate('D, d M Y H:i:s') . ' GMT';
|
|
$hmacAuth = $this->generateHmacAuth('POST', $path, $dateString);
|
|
|
|
$payload = [
|
|
'to_number' => $phoneNumber,
|
|
'to_name' => $userName ?: 'User',
|
|
'message_template_id' => $this->messageTemplateId,
|
|
'channel_integration_id' => $this->channelIntegrationId,
|
|
'language' => [
|
|
'code' => 'id'
|
|
],
|
|
'parameters' => [
|
|
'body' => [
|
|
[
|
|
'key' => '1',
|
|
'value' => 'code',
|
|
'value_text' => $otpCode
|
|
],
|
|
],
|
|
"buttons" => [
|
|
[
|
|
"index" => "0",
|
|
"type" => "url",
|
|
"value" => "$otpCode"
|
|
]
|
|
]
|
|
]
|
|
];
|
|
|
|
log_message('debug', "Mekari WA OTP Request - Date: {$dateString}");
|
|
log_message('debug', "Mekari WA OTP Payload: " . json_encode($payload));
|
|
|
|
return $this->sendRequest($payload, $hmacAuth, $dateString);
|
|
|
|
} catch (\Exception $e) {
|
|
log_message('error', "Mekari WA OTP Exception: " . $e->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Terjadi kesalahan saat mengirim OTP'
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Kirim notifikasi pelanggaran via WhatsApp dengan header image
|
|
*
|
|
* @param string $phoneNumber Nomor WhatsApp tujuan
|
|
* @param array $data Data pelanggaran dengan keys:
|
|
* - nama, jenis_pelanggaran, tanggal_formatted, waktu_formatted
|
|
* - lokasi_kejadian, dasar_hukum, pasal, hukuman, urutan_pelanggaran
|
|
* - pelanggaran_id, is_luar_negeri, image_url (optional), image_filename (optional)
|
|
*/
|
|
public function sendNotifikasiPelanggaran($phoneNumber, $data)
|
|
{
|
|
try {
|
|
$phoneNumber = $this->formatPhoneNumber($phoneNumber);
|
|
$urlParts = parse_url($this->apiUrl);
|
|
$path = $urlParts['path'];
|
|
$dateString = gmdate('D, d M Y H:i:s') . ' GMT';
|
|
$hmacAuth = $this->generateHmacAuth('POST', $path, $dateString);
|
|
|
|
$isLuarNegeri = $data['is_luar_negeri'] ?? false;
|
|
// $templateId = $isLuarNegeri ?
|
|
// $this->messageTemplateIdPelanggaranEN :
|
|
// $this->messageTemplateIdPelanggaranID;
|
|
$templateId = $this->messageTemplateIdPelanggaranID;
|
|
|
|
// Validasi required fields
|
|
$requiredFields = [
|
|
'nama', 'jenis_pelanggaran', 'tanggal_formatted',
|
|
'waktu_formatted', 'lokasi_kejadian', 'dasar_hukum',
|
|
'pasal', 'hukuman', 'urutan_pelanggaran', 'pelanggaran_id'
|
|
];
|
|
|
|
foreach ($requiredFields as $field) {
|
|
if (empty($data[$field])) {
|
|
throw new \Exception("Field '{$field}' is required");
|
|
}
|
|
}
|
|
|
|
// Kalau template inggris sudah ada bisa di uncomment di bawah ini
|
|
// Build body parameters
|
|
// if ($isLuarNegeri) {
|
|
// $bodyParams = [
|
|
// ['key' => '1', 'value' => 'p1', 'value_text' => $data['urutan_pelanggaran']],
|
|
// ['key' => '2', 'value' => 'p2', 'value_text' => $data['title'] ?? 'Mr./Ms.'],
|
|
// ['key' => '3', 'value' => 'p3', 'value_text' => $data['nama']],
|
|
// ['key' => '4', 'value' => 'p4', 'value_text' => $data['jenis_pelanggaran']],
|
|
// ['key' => '5', 'value' => 'p5', 'value_text' => $data['tanggal_formatted']],
|
|
// ['key' => '6', 'value' => 'p6', 'value_text' => $data['waktu_formatted']],
|
|
// ['key' => '7', 'value' => 'p7', 'value_text' => $data['lokasi_kejadian']],
|
|
// ['key' => '8', 'value' => 'p8', 'value_text' => $data['dasar_hukum']],
|
|
// ['key' => '9', 'value' => 'p9', 'value_text' => $data['pasal']],
|
|
// ['key' => '10', 'value' => 'p10', 'value_text' => $data['hukuman']]
|
|
// ];
|
|
// } else {
|
|
// $bodyParams = [
|
|
// ['key' => '1', 'value' => 'p1', 'value_text' => $data['urutan_pelanggaran']],
|
|
// ['key' => '2', 'value' => 'p2', 'value_text' => $data['nama']],
|
|
// ['key' => '3', 'value' => 'p3', 'value_text' => $data['jenis_pelanggaran']],
|
|
// ['key' => '4', 'value' => 'p4', 'value_text' => $data['tanggal_formatted']],
|
|
// ['key' => '5', 'value' => 'p5', 'value_text' => $data['waktu_formatted']],
|
|
// ['key' => '6', 'value' => 'p6', 'value_text' => $data['lokasi_kejadian']],
|
|
// ['key' => '7', 'value' => 'p7', 'value_text' => $data['dasar_hukum']],
|
|
// ['key' => '8', 'value' => 'p8', 'value_text' => $data['pasal']],
|
|
// ['key' => '9', 'value' => 'p9', 'value_text' => $data['hukuman']]
|
|
// ];
|
|
// }
|
|
|
|
// Selalu pakai format Indonesia dulu (9 parameter)
|
|
$bodyParams = [
|
|
['key' => '1', 'value' => 'p1', 'value_text' => $data['urutan_pelanggaran']],
|
|
['key' => '2', 'value' => 'p2', 'value_text' => $data['nama']],
|
|
['key' => '3', 'value' => 'p3', 'value_text' => $data['jenis_pelanggaran']],
|
|
['key' => '4', 'value' => 'p4', 'value_text' => $data['tanggal_formatted']],
|
|
['key' => '5', 'value' => 'p5', 'value_text' => $data['waktu_formatted']],
|
|
['key' => '6', 'value' => 'p6', 'value_text' => $data['lokasi_kejadian']],
|
|
['key' => '7', 'value' => 'p7', 'value_text' => $data['dasar_hukum']],
|
|
['key' => '8', 'value' => 'p8', 'value_text' => $data['pasal']],
|
|
['key' => '9', 'value' => 'p9', 'value_text' => $data['hukuman']]
|
|
];
|
|
|
|
// Build payload parameters
|
|
$parameters = [
|
|
'body' => $bodyParams,
|
|
'buttons' => [
|
|
[
|
|
'index' => '0',
|
|
'type' => 'url',
|
|
'value' => $data['pelanggaran_id']
|
|
]
|
|
]
|
|
];
|
|
|
|
// Tambahkan header
|
|
if (!empty($data['image_url']) && !empty($data['image_filename'])) {
|
|
$parameters['header'] = [
|
|
'format' => 'IMAGE',
|
|
'params' => [
|
|
[
|
|
'key' => 'url',
|
|
'value' => $data['image_url']
|
|
],
|
|
[
|
|
'key' => 'filename',
|
|
'value' => $data['image_filename']
|
|
]
|
|
]
|
|
];
|
|
} else {
|
|
$parameters['header'] = [
|
|
'format' => 'IMAGE',
|
|
'params' => [
|
|
[
|
|
'key' => 'url',
|
|
'value' => "https://cdn.qontak.com/uploads/direct/images/1ed10e3c-461f-477f-a74a-ee6553f23f4d/photo_2025-11-10_15-32-37.jpg"
|
|
],
|
|
[
|
|
'key' => 'filename',
|
|
'value' => "photo_2025-11-10_15-32-37.jpg"
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
$payload = [
|
|
'to_number' => $phoneNumber,
|
|
'to_name' => $data['nama'],
|
|
'message_template_id' => $templateId,
|
|
'channel_integration_id' => $this->channelIntegrationId,
|
|
'language' => [
|
|
'code' => $isLuarNegeri ? 'en' : 'id'
|
|
],
|
|
'parameters' => $parameters
|
|
];
|
|
|
|
log_message('debug', "Mekari WA Pelanggaran Request - Date: {$dateString}");
|
|
log_message('debug', "Mekari WA Pelanggaran Payload: " . json_encode($payload));
|
|
|
|
$result = $this->sendRequest($payload, $hmacAuth, $dateString);
|
|
|
|
if ($result['success']) {
|
|
log_message('info', "WhatsApp notification sent to {$phoneNumber} for pelanggaran {$data['pelanggaran_id']}");
|
|
}
|
|
|
|
return $result;
|
|
|
|
} catch (\Exception $e) {
|
|
log_message('error', "Mekari WA Pelanggaran Exception: " . $e->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Terjadi kesalahan saat mengirim notifikasi pelanggaran: ' . $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
|
|
private function sendRequest($payload, $hmacAuth, $dateString)
|
|
{
|
|
$ch = curl_init($this->apiUrl);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_POST => true,
|
|
CURLOPT_HTTPHEADER => [
|
|
'Authorization: ' . $hmacAuth,
|
|
'Date: ' . $dateString,
|
|
'Content-Type: application/json'
|
|
],
|
|
CURLOPT_POSTFIELDS => json_encode($payload),
|
|
CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_SSL_VERIFYPEER => true
|
|
]);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$error = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($error) {
|
|
log_message('error', "Mekari WA cURL Error: {$error}");
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Gagal terhubung ke server WhatsApp'
|
|
];
|
|
}
|
|
|
|
log_message('debug', "Mekari WA Response [{$httpCode}]: {$response}");
|
|
|
|
$result = json_decode($response, true);
|
|
|
|
if ($httpCode >= 200 && $httpCode < 300) {
|
|
return [
|
|
'success' => true,
|
|
'message' => 'Pesan berhasil dikirim',
|
|
'data' => $result
|
|
];
|
|
}
|
|
|
|
$errorMessage = 'Gagal mengirim pesan';
|
|
if (isset($result['error']['message'])) {
|
|
$errorMessage = $result['error']['message'];
|
|
} elseif (isset($result['message'])) {
|
|
$errorMessage = $result['message'];
|
|
}
|
|
|
|
log_message('error', "Mekari WA Error [{$httpCode}]: " . $response);
|
|
|
|
return [
|
|
'success' => false,
|
|
'message' => $errorMessage,
|
|
'error_code' => $result['error']['code'] ?? $httpCode,
|
|
// 'raw_response' => $result // Untuk debugging
|
|
];
|
|
}
|
|
|
|
private function generateHmacAuth($method, $path, $dateString)
|
|
{
|
|
$requestLine = strtoupper($method) . ' ' . $path . ' HTTP/1.1';
|
|
$stringToSign = 'date: ' . $dateString . "\n" . $requestLine;
|
|
$signature = base64_encode(hash_hmac('sha256', $stringToSign, $this->hmacSecret, true));
|
|
|
|
$hmacHeader = sprintf(
|
|
'hmac username="%s", algorithm="hmac-sha256", headers="date request-line", signature="%s"',
|
|
$this->hmacUsername,
|
|
$signature
|
|
);
|
|
|
|
return $hmacHeader;
|
|
}
|
|
|
|
private function formatPhoneNumber($phone)
|
|
{
|
|
$phone = preg_replace('/[^0-9]/', '', $phone);
|
|
|
|
if (substr($phone, 0, 2) === '62') {
|
|
return $phone;
|
|
}
|
|
|
|
if (substr($phone, 0, 1) === '0') {
|
|
return '62' . substr($phone, 1);
|
|
}
|
|
|
|
return '62' . $phone;
|
|
}
|
|
|
|
public function verifyWebhookSignature($authHeader, $dateHeader, $method, $path)
|
|
{
|
|
try {
|
|
preg_match('/signature="([^"]+)"/', $authHeader, $matches);
|
|
$receivedSignature = $matches[1] ?? '';
|
|
|
|
if (empty($receivedSignature)) {
|
|
return false;
|
|
}
|
|
|
|
$requestLine = strtoupper($method) . ' ' . $path . ' HTTP/1.1';
|
|
$stringToSign = 'date: ' . $dateHeader . "\n" . $requestLine;
|
|
$expectedSignature = base64_encode(hash_hmac('sha256', $stringToSign, $this->hmacSecret, true));
|
|
|
|
return hash_equals($expectedSignature, $receivedSignature);
|
|
|
|
} catch (\Exception $e) {
|
|
log_message('error', "HMAC Verification Error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
} |