Files
api-wipay/src/Controllers/FastController.php

650 lines
24 KiB
PHP

<?php
namespace App\Controllers;
use App\Config\Database;
use App\Helpers\HttpHelper;
use App\Helpers\ResponseHelper;
use App\Models\ApiKeyModel;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
class FastController
{
private $db;
private $apiKeyModel;
public function __construct()
{
$this->db = Database::getInstance();
$this->apiKeyModel = new ApiKeyModel();
}
/**
* GET /fast/test
* Test endpoint (tidak perlu auth)
*/
public function test(Request $request, Response $response): Response
{
$baseUrl = $_ENV['BASE_URL'] ??
($request->getUri()->getScheme() . '://' . $request->getUri()->getHost());
return ResponseHelper::json($response, [
'status' => 'success',
'message' => 'Fast WIPAY API is working!',
'timestamp' => date('Y-m-d H:i:s'),
'controller' => 'Fast',
'method' => 'test',
'url' => $baseUrl . $request->getUri()->getPath()
], 200);
}
/**
* POST /fast/check_bill
* Cek tagihan PDAM
*/
public function checkBill(Request $request, Response $response): Response
{
try {
// Get API key from request attributes (set by middleware)
$apiKey = $request->getAttribute('api_key');
if (!$apiKey) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Invalid API key'
], 401);
}
// Get input from multiple sources
$data = $request->getParsedBody() ?? [];
$query = $request->getQueryParams();
$no_sl = $data['no_sl'] ?? $query['no_sl'] ?? '';
if (empty($no_sl)) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'No SL is required'
], 400);
}
// Get admin user and timo user
$adminUser = $this->db->fetchOne(
"SELECT * FROM admin_users WHERE id = :id LIMIT 1",
['id' => $apiKey->admin_user_id]
);
if (!$adminUser || !$adminUser->timo_user) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Admin user tidak memiliki user TIMO'
], 404);
}
$timoUser = $this->db->fetchOne(
"SELECT * FROM pengguna_timo WHERE id_pengguna_timo = :id LIMIT 1",
['id' => $adminUser->timo_user]
);
if (!$timoUser) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'User TIMO tidak ditemukan'
], 404);
}
// Get WIPAY user (optional for check_bill)
$wipayUser = null;
if ($timoUser->wipay) {
$wipayUser = $this->db->fetchOne(
"SELECT * FROM wipay_pengguna WHERE id_wipay = :id LIMIT 1",
['id' => $timoUser->wipay]
);
}
// Call TIMO API untuk cek tagihan
$timoUrl = 'https://timo.tirtaintan.co.id/enquiry/' . $no_sl;
$timoResponse = HttpHelper::doCurl($timoUrl, 'GET');
// Handle response format - HttpHelper returns object with status/body or decoded JSON
if (!$timoResponse) {
$this->apiKeyModel->logApiUsage($apiKey->id, 'check_bill', 'failed', [
'no_sl' => $no_sl,
'error' => 'Failed to connect to TIMO API'
]);
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Gagal cek tagihan: Tidak dapat menghubungi server TIMO'
], 500);
}
// Convert to array untuk konsistensi (sama seperti TagihanController)
if (is_object($timoResponse)) {
$timoResponse = (array)$timoResponse;
}
// Check for HTTP error status
if (isset($timoResponse['status']) && $timoResponse['status'] != 200) {
$this->apiKeyModel->logApiUsage($apiKey->id, 'check_bill', 'failed', [
'no_sl' => $no_sl,
'error' => $timoResponse['error'] ?? 'HTTP Error'
]);
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Gagal cek tagihan: ' . ($timoResponse['error'] ?? 'HTTP Error')
], $timoResponse['status']);
}
// Get data - bisa dari 'data' key atau langsung dari response (sama seperti TagihanController)
$data = $timoResponse['data'] ?? $timoResponse;
// Format response untuk Fast API (sesuai format API lama)
// Response TIMO API langsung berupa array data, perlu di-wrap dengan errno, error, recordsTotal, data
if (is_array($data) && isset($data[0])) {
// Data adalah array tagihan
$responseData = [
'errno' => 0,
'error' => '',
'recordsTotal' => count($data),
'data' => $data
];
} else {
// Data mungkin sudah dalam format yang benar atau single object
$responseData = [
'errno' => isset($timoResponse['errno']) ? $timoResponse['errno'] : 0,
'error' => $timoResponse['error'] ?? '',
'recordsTotal' => isset($timoResponse['recordsTotal']) ? $timoResponse['recordsTotal'] : (is_array($data) ? count($data) : 1),
'data' => $data
];
}
// Log API usage
$this->apiKeyModel->logApiUsage($apiKey->id, 'check_bill',
$responseData['errno'] == 0 ? 'success' : 'api_error', [
'no_sl' => $no_sl,
'timo_user_id' => $timoUser->id_pengguna_timo,
'wipay_user_id' => $wipayUser ? $wipayUser->id_wipay : null
]);
return ResponseHelper::json($response, [
'status' => 'success',
'data' => $responseData,
'message' => 'Tagihan berhasil dicek'
], 200);
} catch (\Exception $e) {
error_log("Error in checkBill: " . $e->getMessage());
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Gagal cek tagihan: ' . $e->getMessage()
], 500);
}
}
/**
* POST /fast/process_payment
* Proses pembayaran PDAM
*/
public function processPayment(Request $request, Response $response): Response
{
try {
// Get API key
$apiKey = $request->getAttribute('api_key');
if (!$apiKey) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Invalid API key'
], 401);
}
// Get input
$data = $request->getParsedBody() ?? [];
$query = $request->getQueryParams();
$no_sl = $data['no_sl'] ?? $query['no_sl'] ?? '';
$amount = $data['amount'] ?? $query['amount'] ?? 0;
$token = $data['token'] ?? $query['token'] ?? '';
if (empty($no_sl) || empty($amount) || empty($token)) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'No SL, amount, and token are required'
], 400);
}
if (!is_numeric($amount)) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Amount must be a valid number'
], 400);
}
$amount = (float)$amount;
// Get admin user and timo user
$adminUser = $this->db->fetchOne(
"SELECT * FROM admin_users WHERE id = :id LIMIT 1",
['id' => $apiKey->admin_user_id]
);
if (!$adminUser || !$adminUser->timo_user) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Admin user tidak memiliki user TIMO'
], 404);
}
$timoUser = $this->db->fetchOne(
"SELECT * FROM pengguna_timo WHERE id_pengguna_timo = :id LIMIT 1",
['id' => $adminUser->timo_user]
);
if (!$timoUser) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'User TIMO tidak ditemukan'
], 404);
}
// Get WIPAY user
$wipayUser = $this->db->fetchOne(
"SELECT * FROM wipay_pengguna WHERE id_wipay = :id LIMIT 1",
['id' => $timoUser->wipay]
);
if (!$wipayUser) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'User tidak memiliki akun WIPAY'
], 400);
}
// Get current saldo
$saldo = $this->getWipaySaldo($wipayUser->id_wipay);
// Calculate total payment
$biayaAdmin = $timoUser->biaya_admin ?: 0;
$totalPayment = $amount + $biayaAdmin;
// Validate saldo
if ($saldo < $totalPayment) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Saldo WIPAY tidak mencukupi. Saldo: Rp ' . number_format($saldo, 0, ',', '.') . ', Total: Rp ' . number_format($totalPayment, 0, ',', '.')
], 400);
}
// Get tagihan detail from PDAM API
$timoUrl = 'https://timo.tirtaintan.co.id/enquiry/' . $no_sl;
$enquiryResponse = HttpHelper::doCurl($timoUrl, 'GET');
// Handle HTTP error
if (is_object($enquiryResponse) && isset($enquiryResponse->status) && $enquiryResponse->status != 200) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Gagal mendapatkan data tagihan dari PDAM'
], 500);
}
if (!$enquiryResponse || !isset($enquiryResponse->data)) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Data tagihan tidak valid'
], 500);
}
// Prepare payment data for PDAM
$pdamData = [];
foreach ($enquiryResponse->data as $d) {
$pdamData[] = [
'rek_nomor' => $d->rek_nomor ?? $d->rek_no ?? '',
'rek_total' => $d->rek_total ?? 0,
'serial' => '#TM' . time(),
'byr_tgl' => date('YmdHis'),
'loket' => 'TIMO',
];
}
$paymentPost = [
'token' => $token,
'data' => $pdamData
];
// Send payment to PDAM API
$timoPaymentUrl = 'https://timo.tirtaintan.co.id/payment/' . $token;
$paymentResponse = HttpHelper::doCurl($timoPaymentUrl, 'POST', $paymentPost, true);
if ($paymentResponse && isset($paymentResponse->errno) && $paymentResponse->errno == 0) {
// Payment successful - Record to database
$pembayaranData = [
'no_trx' => '#TIMO' . $token,
'token' => $adminUser->timo_user,
'no_sl' => $no_sl,
'nama_bank' => 'WIPAY',
'no_rekening' => $wipayUser->no_hp ?? '',
'jumlah_tagihan' => (string)$amount,
'biaya_admin' => (string)$biayaAdmin,
'jumlah_unik' => '0',
'promo' => '0',
'raw_data' => json_encode($enquiryResponse->data),
'waktu_expired' => date('Y-m-d H:i:s', strtotime('+1 days')),
'status_bayar' => 'DIBAYAR',
'tanggal_bayar' => date('Y-m-d H:i:s'),
'jumlah_bayar' => (string)$totalPayment,
'bukti_transfer' => '',
'tanggal_request' => date('Y-m-d H:i:s'),
'respon_wa' => '',
'admin_2' => '0',
'raw_bayar' => json_encode($paymentResponse),
'banyak_cek' => '0'
];
$pembayaranId = $this->db->insert('pembayaran', $pembayaranData);
// Deduct WIPAY saldo
$this->db->insert('wipay_mutasi', [
'wipay_user' => $wipayUser->id_wipay,
'waktu_transaksi' => date('Y-m-d H:i:s'),
'jumlah_mutasi' => $totalPayment * -1,
'saldo_akhir' => $saldo - $totalPayment,
'ket_mutasi' => "PEMBAYARAN PDAM SL $no_sl via Fast API",
'detail_transaksi' => serialize($pembayaranData),
'sumber_transaksi' => 'TRANSAKSI',
]);
// Update WIPAY saldo
$this->db->update('wipay_pengguna', [
'saldo' => $saldo - $totalPayment
], 'id_wipay = :id', ['id' => $wipayUser->id_wipay]);
// Log API usage
$this->apiKeyModel->logApiUsage($apiKey->id, 'process_payment', 'success', [
'no_sl' => $no_sl,
'amount' => $amount,
'token' => $token,
'pembayaran_id' => $pembayaranId
]);
return ResponseHelper::json($response, [
'status' => 'success',
'message' => 'Pembayaran berhasil diproses',
'data' => [
'pembayaran_id' => $pembayaranId,
'no_trx' => $pembayaranData['no_trx'],
'no_sl' => $no_sl,
'amount' => $amount,
'biaya_admin' => $biayaAdmin,
'total_payment' => $totalPayment,
'saldo_akhir' => $saldo - $totalPayment,
'status' => 'DIBAYAR',
'tanggal_pembayaran' => date('Y-m-d H:i:s')
]
], 200);
} else {
// Payment failed
$this->apiKeyModel->logApiUsage($apiKey->id, 'process_payment', 'failed', [
'no_sl' => $no_sl,
'amount' => $amount,
'token' => $token,
'error' => json_encode($paymentResponse)
]);
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Gagal proses pembayaran ke PDAM: ' . ($paymentResponse->error ?? 'Unknown error')
], 500);
}
} catch (\Exception $e) {
error_log("Error in processPayment: " . $e->getMessage());
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Gagal proses pembayaran: ' . $e->getMessage()
], 500);
}
}
/**
* GET /fast/process_payment_get
* Proses pembayaran via GET (temporary workaround)
*/
public function processPaymentGet(Request $request, Response $response): Response
{
// Same logic as processPayment but get data from query params
$query = $request->getQueryParams();
$request = $request->withParsedBody($query);
return $this->processPayment($request, $response);
}
/**
* GET /fast/payment_status
* Cek status pembayaran
*/
public function paymentStatus(Request $request, Response $response): Response
{
try {
// Get API key
$apiKey = $request->getAttribute('api_key');
if (!$apiKey) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Invalid API key'
], 401);
}
// Get transaction_id
$data = $request->getParsedBody() ?? [];
$query = $request->getQueryParams();
$transactionId = $data['transaction_id'] ?? $query['transaction_id'] ?? '';
if (empty($transactionId)) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Transaction ID is required'
], 400);
}
// Check payment status
$payment = $this->db->fetchOne(
"SELECT * FROM pembayaran WHERE id_pembayaran = :id LIMIT 1",
['id' => $transactionId]
);
if ($payment) {
return ResponseHelper::json($response, [
'status' => 'success',
'data' => [
'transaction_id' => $payment->id_pembayaran,
'no_sl' => $payment->no_sl,
'amount' => $payment->jumlah_tagihan,
'status' => $payment->status_bayar,
'created_at' => $payment->tanggal_request
]
], 200);
} else {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Transaction not found'
], 404);
}
} catch (\Exception $e) {
error_log("Error in paymentStatus: " . $e->getMessage());
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Gagal cek status pembayaran: ' . $e->getMessage()
], 500);
}
}
/**
* GET /fast/check_wipay_saldo
* Cek saldo WIPAY
*/
public function checkWipaySaldo(Request $request, Response $response): Response
{
try {
// Get API key
$apiKey = $request->getAttribute('api_key');
if (!$apiKey) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Invalid API key'
], 401);
}
// Get admin user and timo user
$adminUser = $this->db->fetchOne(
"SELECT * FROM admin_users WHERE id = :id LIMIT 1",
['id' => $apiKey->admin_user_id]
);
if (!$adminUser || !$adminUser->timo_user) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Admin user tidak memiliki data TIMO'
], 404);
}
$timoUser = $this->db->fetchOne(
"SELECT * FROM pengguna_timo WHERE id_pengguna_timo = :id LIMIT 1",
['id' => $adminUser->timo_user]
);
if (!$timoUser) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'User TIMO tidak ditemukan'
], 404);
}
if (!$timoUser->wipay || $timoUser->wipay <= 0) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'User tidak memiliki akun WIPAY'
], 400);
}
$wipayUser = $this->db->fetchOne(
"SELECT * FROM wipay_pengguna WHERE id_wipay = :id LIMIT 1",
['id' => $timoUser->wipay]
);
if (!$wipayUser) {
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Data WIPAY user tidak ditemukan'
], 400);
}
$saldo = $this->getWipaySaldo($wipayUser->id_wipay);
// Log API usage
$this->apiKeyModel->logApiUsage($apiKey->id, 'check_wipay_saldo', 'success', [
'user_id' => $apiKey->admin_user_id,
'wipay_user_id' => $wipayUser->id_wipay
]);
return ResponseHelper::json($response, [
'status' => 'success',
'message' => 'Saldo WIPAY berhasil dicek',
'data' => [
'user_id' => $apiKey->admin_user_id,
'wipay_user_id' => $wipayUser->id_wipay,
'nama_lengkap' => $wipayUser->nama_lengkap ?? '',
'no_hp' => $wipayUser->no_hp ?? '',
'saldo' => $saldo,
'saldo_formatted' => 'Rp ' . number_format($saldo, 0, ',', '.'),
'biaya_admin' => $timoUser->biaya_admin ?: 0
]
], 200);
} catch (\Exception $e) {
error_log("Error in checkWipaySaldo: " . $e->getMessage());
return ResponseHelper::json($response, [
'status' => 'error',
'message' => 'Gagal cek saldo WIPAY: ' . $e->getMessage()
], 500);
}
}
/**
* GET /fast/check_wipay_saldo_get
* Cek saldo WIPAY via GET
*/
public function checkWipaySaldoGet(Request $request, Response $response): Response
{
// Same as checkWipaySaldo
return $this->checkWipaySaldo($request, $response);
}
/**
* GET /fast/mandiri/{tanggal}
* Data Mandiri (mirip dengan /api/mandiri)
*/
public function mandiri(Request $request, Response $response, array $args): Response
{
$tanggal = $args['tanggal'] ?? '';
if (empty($tanggal)) {
$response->getBody()->write('DATE NOT SPECIFIED');
return $response->withStatus(400);
}
// Parse tanggal format ddmmyyyy
$format = "dmY";
$date = \DateTime::createFromFormat($format, $tanggal);
if ($date) {
$tanggal_cari = $date->format('Y-m-d');
} else {
$tanggal_cari = date('Y-m-d');
}
// Get base URL
$baseUrl = $_ENV['BASE_URL'] ??
($request->getUri()->getScheme() . '://' . $request->getUri()->getHost());
// Query data
$sql = "SELECT cm.no_sl, pt.no_hp, cm.tanggal_catat as tanggal_baca,
cm.angka_meter,
CONCAT(:base_url, '/assets/uploads/catat_meter/', cm.photo) as photo
FROM catat_meter cm
LEFT JOIN pengguna_timo pt ON cm.token = pt.id_pengguna_timo
WHERE DATE(cm.tanggal_catat) = :tanggal_cari";
$data = $this->db->fetchAll($sql, [
'base_url' => $baseUrl,
'tanggal_cari' => $tanggal_cari
]);
return ResponseHelper::json($response, [
'status' => 1,
'date' => $tanggal,
'data' => $data
], 200);
}
/**
* Get WIPAY saldo
*/
private function getWipaySaldo($wipayUserId)
{
// Get latest saldo from mutasi or wipay_pengguna
$mutasi = $this->db->fetchOne(
"SELECT saldo_akhir FROM wipay_mutasi
WHERE wipay_user = :id
ORDER BY waktu_transaksi DESC LIMIT 1",
['id' => $wipayUserId]
);
if ($mutasi && isset($mutasi->saldo_akhir)) {
return (float)$mutasi->saldo_akhir;
}
// Fallback to wipay_pengguna saldo
$wipayUser = $this->db->fetchOne(
"SELECT saldo FROM wipay_pengguna WHERE id_wipay = :id LIMIT 1",
['id' => $wipayUserId]
);
return $wipayUser ? (float)($wipayUser->saldo ?? 0) : 0;
}
}