company = $company; $this->setupBillplzClient($company); $this->setupUrls(); $this->paymentTitle = config('services.billplz.paymentTitle'); } public function getCollections() { return $this->client->collection()->all()->toArray(); } public function createBill(array $data,string $company) { $composed_data = [ $this->config['collection_id'], ...$data, $this->callbackUrl, $this->paymentTitle, $this->redirectUrl, ]; try{ $response = $this->client->bill()->create(...$composed_data)->toArray(); OnlineTransactionLoggerService::logs($response['id'], $composed_data, $response, TransactionOnlineStatus::BILL_CREATED); return $response; } catch(\Exception $e){ if($e->getCode() === 404){ $errorMessage = 'Please check your billplz payment gateway collection. Ensure that api_key, x_signature, and collection_id are correctly entered'; OnlineTransactionLoggerService::logs( null, $composed_data, $errorMessage, TransactionOnlineStatus::BILL_CREATED); throw new \ErrorException($errorMessage); } else { OnlineTransactionLoggerService::logs( null, $composed_data, $e->getMessage(), TransactionOnlineStatus::BILL_CREATED); }; throw $e; } } public function redirect($request) { try{ $response = $this->client->bill()->redirect($request); OnlineTransactionLoggerService::logs($request['billplz']['id'], $request, $response, TransactionOnlineStatus::UPDATE_TRANSACTION_REDIRECT); return $response; } catch(\Exception $e){ OnlineTransactionLoggerService::logs($request['billplz']['id'], $request, $e->getMessage(), TransactionOnlineStatus::UPDATE_TRANSACTION_REDIRECT); throw $e; } } public function webhook($request) { // $this->setCompany(Session::get('company')); // $this->setupBillplzClient($this->company); try { $response = $this->client->webhook($request); OnlineTransactionLoggerService::logs($request['billplz']['id'], $request, $response, TransactionOnlineStatus::UPDATE_TRANSACTION_CALLBACK); return $response; } catch (\Exception $e) { OnlineTransactionLoggerService::logs($request['billplz']['id'], $request, $e->getMessage(), TransactionOnlineStatus::UPDATE_TRANSACTION_CALLBACK); throw $e; } } public function getTransactions($date) { $arrahnTransactions = $this->getArrahnTransactions(['date' => $date]); $billplzID = collect($arrahnTransactions)->pluck('billplz_id')->unique()->values(); $billplzTransactions = collect($billplzID)->map(function ($billId) { return $this->client->transaction()->get($billId)->toArray(); }) ->map(function ($data) { $latestTransaction = reset($data['transactions']) == false ? [] : reset($data['transactions']); $billId = ['bill_id' => $data['bill_id']]; return array_merge($billId, $latestTransaction); }) ->keyBy('bill_id'); return $billplzTransactions; } public function getTransactionsByBillId($billId) { $billplzID = collect([$billId]); $billplzTransactions = collect($billplzID)->map(function ($billId) { return $this->client->transaction()->get($billId)->toArray(); }) ->map(function ($data) { $latestTransaction = reset($data['transactions']) == false ? [] : reset($data['transactions']); $billId = ['bill_id' => $data['bill_id']]; return array_merge($billId, $latestTransaction); }) ->keyBy('bill_id'); return $billplzTransactions; } public function getArrahnTransactions($queryData) { $query_all = []; if(isset($queryData['date'])){ $pkb_query = Http::arrahn()->get('/api/transaction/online/query', ['date' => $queryData['date']])->json(); $koop_query = Http::arrahn_koop()->get('/api/transaction/online/query', ['date' => $queryData['date']])->json(); $query_all = array_merge($pkb_query,$koop_query); } if(isset($queryData['billId'])){ $pkb_query = Http::arrahn()->get('/api/transaction/online/query', ['billId' => $queryData['billId']])->json(); $koop_query = Http::arrahn_koop()->get('/api/transaction/online/query', ['billId' => $queryData['billId']])->json(); $query_all = array_merge($pkb_query,$koop_query); } return $query_all; } public function getBillplzTransactionReview($date) { $arrahnTransactions = $this->getArrahnTransactions(['date' => $date]); // sort array by tarikh desc and masa key desc array_multisort(array_column($arrahnTransactions, 'tarikh'), SORT_DESC, array_column($arrahnTransactions, 'masa'), SORT_DESC, $arrahnTransactions); $arrahnTransactionsGroupedByBill = collect($arrahnTransactions)->groupBy('billplz_id'); $billplzID = $arrahnTransactionsGroupedByBill->keys(); $billplzTransactions = $this->getTransactions($date); $merged = $billplzID->map(function ($billId) use ($arrahnTransactionsGroupedByBill, $billplzTransactions) { return [ 'bill_id' => $billId, 'arrahn_transactions' => $arrahnTransactionsGroupedByBill->get($billId)->toArray(), 'billplz_latest_transaction' => $billplzTransactions->get($billId), ]; }); $summarizedBillplzTransactions = collect($merged)->map(function ($data) { $data['arrahn_transactions'] = collect($data['arrahn_transactions'])->map(function ($transaction) { return[ 'nokp' => $transaction['nokp'], 'norujukan' => $transaction['norujukan'], 'kodcaw' => $transaction['kodcaw'], 'total_payment' => $transaction['total_payment'], 'customer_name' => $transaction['customer_name'], 'status' => $transaction['status'], 'paid_at' => $transaction['paid_at'], 'tarikh_attempt_bayar' => $transaction['tarikh'], 'masa_attempt_bayar' => $transaction['masa'], 'portal' => $transaction['tagportal'] == 1 ? 'Arrahn Pay' : 'SPARE', ]; }); return $data; }); return $summarizedBillplzTransactions; } public function getBillplzTransactionReviewByBillId($billId) { $arrahnTransactions = $this->getArrahnTransactions(['billId' => $billId]); $billplzID = collect([$billId]); $billplzTransactions = $this->getTransactionsByBillId($billId); $merged = $billplzID->map(function ($billId) use ($billplzTransactions, $arrahnTransactions) { return [ 'bill_id' => $billId, 'arrahn_transactions' => $arrahnTransactions, 'billplz_latest_transaction' => $billplzTransactions->get($billId), ]; }); $summarizedBillplzTransactions = collect($merged)->map(function ($data) { $data['arrahn_transactions'] = collect($data['arrahn_transactions'])->map(function ($transaction) { return[ 'nokp' => $transaction['nokp'], 'norujukan' => $transaction['norujukan'], 'kodcaw' => $transaction['kodcaw'], 'total_payment' => $transaction['total_payment'], 'customer_name' => $transaction['customer_name'], 'status' => $transaction['status'], 'paid_at' => $transaction['paid_at'], 'tarikh_attempt_bayar' => $transaction['tarikh'], 'masa_attempt_bayar' => $transaction['masa'], 'portal' => $transaction['tagportal'] == 1 ? 'Arrahn Pay' : 'SPARE', ]; }); return $data; }); return $summarizedBillplzTransactions; } public function getTransactionByBillId($billId,$company) { $appEnvironment = config('app.env'); $billplzEnvironments = [ 'PKB' => ($appEnvironment === 'production') ? config('services.billplz.production') : config('services.billplz.sandbox'), 'KOPKB' => ($appEnvironment === 'production') ? config('services.billplz.production_kopkb') : config('services.billplz.sandbox_kopkb'), ]; $billplzConfig = $appEnvironment === 'production' ? ($billplzEnvironments[$company] ?? config('services.billplz.production')) : ($billplzEnvironments[$company] ?? config('services.billplz.sandbox')); $client = Client::make($billplzConfig['api_key'], ($this->isBase64($billplzConfig['x_signature'])) ? $billplzConfig['x_signature'] : base64_encode($billplzConfig['x_signature'])); $client->useVersion('v4'); try { return $this->client->bill()->transaction($billId)->toArray(); } catch (\Throwable $th) { logger()->error('Error when querying billplz id : ' . $billId); logger()->error($th->getMessage()); throw $th; } } public function getBills($billIds) { return array_map(fn($x) => $this->client->bill()->get($x)->toArray(), $billIds); } protected function setupBillplzClient($company) { $appEnvironment = config('app.env'); $billplzEnvironments = [ 'PKB' => ($appEnvironment === 'production') ? config('services.billplz.production') : config('services.billplz.sandbox'), 'KOPKB' => ($appEnvironment === 'production') ? config('services.billplz.production_kopkb') : config('services.billplz.sandbox_kopkb'), ]; $billplzConfig = $appEnvironment === 'production' ? ($billplzEnvironments[$company] ?? config('services.billplz.production')) : ($billplzEnvironments[$company] ?? config('services.billplz.sandbox')); $client = Client::make($billplzConfig['api_key'], ($this->isBase64($billplzConfig['x_signature'])) ? $billplzConfig['x_signature'] : base64_encode($billplzConfig['x_signature'])); $client->useVersion('v4'); if ($appEnvironment !== 'production') { $client->useSandbox(); } $this->config = $billplzConfig; $this->client = $client; } protected function setupUrls() { $this->callbackUrl = route('pay.callback'); $this->redirectUrl = ['redirect_url' => route('pay.paymentStatus')]; } protected function isBase64($string) { if($this->isBase64UrlSafe($string)){ return true; } if (preg_match('/^[A-Za-z0-9+\/=]+$/', $string)) { return base64_encode(base64_decode($string, true)) === $string; } return false; } protected function isBase64UrlSafe($string) { // Convert URL-safe Base64 to standard Base64 $standardBase64 = str_replace(['-', '_'], ['+', '/'], $string); // Add padding if necessary $remainder = strlen($standardBase64) % 4; if ($remainder > 0) { $standardBase64 .= str_repeat('=', 4 - $remainder); } // Attempt to decode the string $decoded = base64_decode($standardBase64, true); return $decoded !== false; // Return true if it successfully decodes } }