Initial commit

This commit is contained in:
Kwesi Banson Jnr
2026-04-08 05:53:02 +00:00
commit 592a161ee6
63 changed files with 4105 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Controllers;
use App\Models\User;
use App\Core\Auth;
use App\Core\Validator;
use App\Core\Controller;
class AuthController extends Controller {
public function login() {
$errors = Validator::validate($_POST, [
'email' => 'required|email',
'password' => 'required'
]);
if (!empty($errors)) {
return $this->render('auth/login', ['errors' => $errors]);
}
// Check database for user
$user = User::findByEmail($_POST['email']); // Custom method in User model
if ($user && password_verify($_POST['password'], $user['password'])) {
Auth::login($user);
header('Location: /dashboard');
} else {
return $this->render('auth/login', ['error' => 'Invalid credentials']);
}
}
}
?>

View File

@@ -0,0 +1,397 @@
<?php
namespace App\Controllers;
use App\Core\Controller;
use App\Models\User;
use App\Models\Disbursement;
use App\Models\AirtelBroker;
use App\Models\MpambaBroker;
use App\Core\Auth;
use App\Core\Logger;
class DisbursementController extends Controller {
public function index() {
#$users = User::all();
$token = Auth::getBearerToken();
$user = User::getByToken($token);
$logger = new Logger();
if ($user == false) {
// code...
$logger->error("API Authentication failed token : " . $token);
return $this->json([
"status" => "failed",
"message" => "Unauthorised Access",
]);
}
$log_data = json_encode($user);
$logger->info("User $log_data successfully authenticated from " . $_SERVER['REMOTE_ADDR']);
$payload = json_decode(file_get_contents("php://input") , true);
if (json_last_error() !== JSON_ERROR_NONE) {
// echo "JSON Error: " . json_last_error_msg();
http_response_code(400);
return $this->json([
"status" => "failed",
"message" => "Bad Request. Invalid JSON Object",
]);
}
$requested_keys = ['transaction_id', 'reference_id', 'mobile_number', 'amount'];
foreach ($requested_keys as $key) {
if (!isset($payload[$key]) || trim($payload[$key]) === '') {
http_response_code(400);
return $this->json([
"status" => "failed",
"message" => "Validation Error: Missing or empty field : {$key}",
]);
}
}
if (!preg_match('/^[a-zA-Z0-9]+$/', $payload['transaction_id'])) {
http_response_code(400);
return $this->json([
"status" => "failed",
"message" => "Validation Error: Malformed Transaction ID",
]);
}
if (!preg_match('/^[a-zA-Z0-9]+$/', $payload['reference_id'])) {
return $this->json([
"status" => "failed",
"message" => "Validation Error: Malformed Reference ID",
]);
}
if (!preg_match('/^[0-9]{9,12}$/', $payload['mobile_number'])) {
return $this->json([
"status" => "failed",
"message" => "Validation Error: Invalid Mobile Number format",
]);
}
$amount = filter_var($payload['amount'], FILTER_VALIDATE_FLOAT);
if ($amount === false || $amount <= 0) {
return $this->json([
"status" => "failed",
"message" => "Validation Error: Amount must be a positive number",
]);
}
$logger->info("Disbursement check request : " . json_encode($payload));
$transaction_details = Disbursement::varifyTransaction($payload['transaction_id'], $payload['reference_id']);
if ($transaction_details == false) {
return $this->json([
'status' => 'fail',
'data' => 'Transaction not found',
]);
}
$network = $this->getNetwork($payload['mobile_number']);
//pull this away into the ENV file
$pin = "IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=";
if ($network == 'airtel') {
$broker_details = AirtelBroker::find($transaction_details[0]['wallet_id']);
$authURL = $_ENV['AIRTEL_MONEY_AUTH_URL'];
$auth_result = $this->authenticateAirtel($authURL, $broker_details['client_id'], $broker_details['client_secret']);
if ($auth_result['success'] == false) {
$logger->info("Airtel Money authentication failed " . json_encode($auth_result));
return $this->json([
'status' => 'fail',
'data' => 'Your request could not be handled at this time. Try again',
]);
}
$airtel_array = [
"accessToken" => $auth_result['token'],
"msisdn" => $transaction_details[0]['msisdn'],
"amount" => $amount,
"reference" => $transaction_details[0]['infotech_transaction_id'],
"pin" => $pin,
"transactionId" => $transaction_details[0]['transaction_id'],
"country" => "MW",
"currency" => "MWK"
];
$retval = $this->airtelDisbursement($airtel_array);
var_dump($retval);
$disbursement_update_data = [
'response_message' => $disbursement_retval['response'],
'confirmed' => 1,
'confirmed_at' => date('Y-m-d H:i:s'),
'status' => 'successful'
];
$logger->info('Disbursement Update data : ' . json_encode($disbursement_update_data));
$update_result = Disbursement::updateById($transaction_details[0]['id'], $disbursement_update_data);
$logger->info('Airtel Disbursement Update Result : ' . $update_result);
}
elseif($network == 'tnm'){
//code
$broker_details = MpambaBroker::getBroker($transaction_details[0]['wallet_id']);
$auth_result = $this->authenticateMpamba($transaction_details[0]['wallet_id'], $broker_details[0]['password']);
$params = [
"msisdn" => $transaction_details[0]['msisdn'],
"amount" => $transaction_details[0]['amount'],
"transaction_id" => $transaction_details[0]['transaction_id'],
"narration" => "Testing", //$transaction_details[0]['description']
"token" => $auth_result['token']
];
$disbursement_retval = $this->mpambaDisbursement($params);
var_dump($disbursement_retval);
$disbursement_update_data = [
'response_message' => $disbursement_retval['raw'],
'confirmed' => 1,
'confirmed_at' => date('Y-m-d H:i:s'),
'status' => 'successful'
];
$logger->info('Disbursement Update data : ' . json_encode($disbursement_update_data));
$update_result = Disbursement::updateById($transaction_details[0]['id'], $disbursement_update_data);
$logger->info('Mpamba Disbursement Update Result : ' . $update_result);
}
else{
return $this->json([
'status' => 'fail',
'data' => 'Invalid phone number',
]);
}
}
public function show($id) {
$user = User::find($id);
if (!$user) {
return $this->json(['message' => 'User not found'], 404);
}
return $this->json($user);
}
public function testUpdate(){
$disbursement_update_data = [
'response_message' => 'in heere',
'confirmed' => 1,
'confirmed_at' => date('Y-m-d H:i:s'),
'status' => 'successful'
];
$update_result = Disbursement::updateById('3', $disbursement_update_data);
var_dump($update_result);
}
public function airtelDisbursement($params) {
$timeout = 30;
$logger = new Logger();
$url = $_ENV['AIRTEL_MONEY_DISBURSEMENT_URL'];
$payload = [
"payee" => [
"msisdn" => $params['msisdn']
],
"reference" => $params['reference'],
"pin" => $params['pin'],
"transaction" => [
"amount" => $params['amount'],
"id" => $params['transactionId']
]
];
$log_data = json_encode($payload);
$logger->info("Disbursement Requests to Airtel MW : " . $log_data );
$headers = [
'Content-Type: application/json',
'Accept: */*',
'X-Country: ' . $params['country'],
'X-Currency: ' . $params['currency'],
'Authorization: Bearer ' . $params['accessToken']
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_SSL_VERIFYPEER => true
]);
$response = curl_exec($ch);
$error = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($error) {
return [
'success' => false,
'error' => 'CURL_ERROR',
'message' => $error
];
}
$logger->info("Disbursement Response from Airtel MW : " . $response);
return [
'success' => ($code === 200),
'http_code' => $code,
'response' => json_decode($response, true),
'raw' => $response
];
}
public function authenticateAirtel($baseURL, $wallet, $password){
// JSON payload
$postData = json_encode([
"client_id" => $wallet,
"client_secret" => $password,
"grant_type" => "client_credentials"
]);
if (json_last_error() !== JSON_ERROR_NONE) {
return [
"status" => "failed",
"message" => "Bad Request. Invalid JSON Object" . json_last_error_msg(),
];
}
$ch = curl_init($baseURL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Content-Length: ' . strlen($postData)
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
$curl_error = curl_error($ch);
curl_close($ch);
return [
'success' => false,
'error' => 'Curl error: ' . $curl_error
];
}
// Get HTTP status code
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Decode JSON response
$result = json_decode($response, true);
// Check if token is present
if ($httpCode === 200 && isset($result['access_token'])) {
return [
'success' => true,
'token' => $result['access_token']
];
} else {
return [
'success' => false,
'error' => $result['error_description'] ?? 'Unknown error',
'details' => $result['error'] ?? []
];
}
}
#Mpamba Scripts
public function authenticateMpamba($wallet, $password){
$logger = new Logger();
$url = rtrim($_ENV['MPAMBA_BASE_URL'], '/') . '/authenticate';
// JSON payload
$postData = json_encode([
'wallet' => $wallet,
'password' => $password
]);
$logger->info("Mpamba Authentication Requests : " . $postData );
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Return the response
curl_setopt($ch, CURLOPT_POST, true); // Use POST method
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);// Set the request body
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Content-Length: ' . strlen($postData)
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
$curl_error = curl_error($ch);
$logger->info("Mpamba Authentication Error : " . $curl_error );
curl_close($ch);
return [
'success' => false,
'error' => 'Error: ' . $curl_error
];
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Decode JSON response
$result = json_decode($response, true);
// Check if token is present
$logger->info("Mpamba Authentication Response : " . json_encode($result));
if ($httpCode === 200 && isset($result['data']['token'])) {
return [
'success' => true,
'token' => $result['data']['token'],
'expires_at' => $result['data']['expires_at']
];
} else {
return [
'success' => false,
'error' => $result['message'] ?? 'Unknown error',
'details' => $result['errors'] ?? []
];
}
}
public function mpambaDisbursement($params){
$logger = new Logger();
$url = $_ENV['MPAMBA_BASE_URL'] . "/payments";
$payload = array(
"msisdn" => $params['msisdn'],
"amount" => (int)$params['amount'],
"transaction_id" => $params['transaction_id'],
"narration" => $params['narration']
);
$logger->info("Mpamba Disbursement Requests : " . json_encode($payload));
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Content-Type: application/json",
"Authorization: Bearer " . $params['token']
));
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
$logger->info("Mpamba Disbursement Response Error : " . $error);
curl_close($ch);
return array(
"status" => "ERROR",
"message" => "Error: " . $error
);
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$logger->info("Mpamba Disbursement Response : " . $response);
$responseData = json_decode($response, true);
return array(
"http_code" => $httpCode,
"response" => $responseData
);
}
}
?>

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Controllers;
use App\Core\Controller;
use App\Models\User;
class HomeController extends Controller {
public function index() {
echo json_encode(['code' => 1, 'msg' => 'heere at the wall']);
}
}
?>

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Controllers;
use App\Core\Controller;
use App\Models\User;
class UserController extends Controller {
public function index() {
// 1. Get data from a Model
$users = User::all();
// 2. Pass data to the view via the base render() method
return $this->render('users/index', [
'title' => 'All Users',
'users' => $users
]);
}
public function store() {
User::create([
'username' => $_POST['username'],
'email' => $_POST['email'],
'password' => password_hash($_POST['password'], PASSWORD_DEFAULT)
]);
header('Location: /users');
}
// Update a user
public function update() {
User::updateById($_POST['id'], [
'email' => $_POST['email']
]);
}
// Delete a user
public function destroy() {
User::deleteById($_GET['id']);
}
}
?>