Upload files to "system/plugin"

Signed-off-by: nestict <icttechnest@gmail.com>
This commit is contained in:
nestict 2025-05-24 12:34:11 +02:00
parent fd6a88f443
commit 2a327ddd3d
5 changed files with 1106 additions and 0 deletions

BIN
system/plugin/.DS_Store vendored Normal file

Binary file not shown.

2
system/plugin/.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

View File

@ -0,0 +1,420 @@
<?php
function Alloworigins()
{
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type");
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
exit;
}
$requestUri = $_SERVER['REQUEST_URI'];
$queryString = parse_url($requestUri, PHP_URL_QUERY);
$type = null;
if ($queryString) {
parse_str($queryString, $queryParameters);
if (isset($queryParameters['type'])) {
$type = $queryParameters['type'];
if ($type === "grant") {
CreateHostspotUser();
exit;
} elseif ($type === "verify") {
VerifyHotspot();
exit;
} elseif ($type === "reconnect") {
ReconnectUser();
exit;
} elseif ($type === "voucher") {
ReconnectVoucher();
exit;
} else {
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'The parameter is not present in the URL.']);
}
}
}
}
function ReconnectVoucher() {
header('Content-Type: application/json');
$rawData = file_get_contents('php://input');
$postData = json_decode($rawData, true);
if (!isset($postData['voucher_code'], $postData['account_id'])) {
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'Missing accountId or voucherCode field']);
return;
}
$accountId = $postData['account_id'];
$voucherCode = $postData['voucher_code'];
$voucher = ORM::for_table('tbl_voucher')
->where('code', $voucherCode)
->where('status', '0')
->find_one();
if (!$voucher) {
echo json_encode([
'status' => 'error',
'Resultcode' => '1',
'voucher' => 'Not Found',
'message' => 'Invalid Voucher code'
]);
exit();
}
if ($voucher['status'] == '1') {
echo json_encode([
'status' => 'error',
'Resultcode' => '3',
'voucher' => 'Used',
'message' => 'Voucher code is already used'
]);
exit();
}
$planId = $voucher['id_plan'];
$routername = $voucher['routers'];
$router = ORM::for_table('tbl_routers')
->where('name', $routername)
->find_one();
if (!$router) {
echo json_encode([
'status' => 'error',
'message' => 'Router not found'
]);
exit();
}
$routerId = $router['id'];
if (!ORM::for_table('tbl_plans')->where('id', $planId)->count() || !ORM::for_table('tbl_routers')->where('id', $routerId)->count()) {
echo json_encode([
'status' => 'error',
'message' => 'Unable to process your request, please refresh the page'
]);
exit();
}
$user = ORM::for_table('tbl_customers')->where('username', $accountId)->find_one();
if (!$user) {
// Create a new user if not exists
$user = ORM::for_table('tbl_customers')->create();
$user->username = $accountId;
$user->password = '1234';
$user->fullname = $accountId;
$user->email = $accountId . '@gmail.com';
$user->phonenumber = $accountId;
$user->pppoe_password = '1234';
$user->address = '';
$user->service_type = 'Hotspot';
}
$user->router_id = $routerId;
$user->save();
// Update the voucher with the user ID
$voucher->user = $user->id;
$voucher->status = '1'; // Mark as used
$voucher->save();
if (Package::rechargeUser($user->id, $routername, $planId, 'Voucher', $voucherCode)) {
echo json_encode([
'status' => 'success',
'Resultcode' => '2',
'voucher' => 'activated',
'message' => 'Voucher code has been activated',
'username' => $user->username
]);
} else {
echo json_encode([
'status' => 'error',
'message' => 'Failed to recharge user package'
]);
}
}
function ReconnectUser()
{
header('Content-Type: application/json');
$rawData = file_get_contents('php://input');
$postData = json_decode($rawData, true);
if (!$postData) {
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'Invalid JSON DATA']);
exit();
}
if (!isset($postData['mpesa_code'])) {
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'missing required fields']);
exit();
}
$mpesaCode = $postData['mpesa_code'];
// Query the payment gateway table
$payment = ORM::for_table('tbl_payment_gateway')
->where('gateway_trx_id', $mpesaCode)
->find_one();
if (!$payment) {
$data = array(['status' => 'error', "Resultcode" => "1", 'user' => "Not Found", 'message' => 'Invalid Mpesa Transaction code']);
echo json_encode($data);
exit();
}
$username = $payment['username'];
// Query the user recharges table
$recharge = ORM::for_table('tbl_user_recharges')
->where('username', $username)
->order_by_desc('id')
->find_one();
if ($recharge) {
$status = $recharge['status'];
if ($status == 'on') {
$data = array(
"Resultcode" => "2",
"user" => "Active User",
"username" => $username,
"tyhK" => "1234", // Replace with the actual password or token
"Message" => "We have verified your transaction under the Mpesa Transaction $mpesaCode. Please don't leave this page as we are redirecting you.",
"Status" => "success"
);
} elseif ($status == "off") {
$data = array(
"Resultcode" => "3",
"user" => "Expired User",
"Message" => "We have verified your transaction under the Mpesa Transaction $mpesaCode. But your Package is already Expired. Please buy a new Package.",
"Status" => "danger"
);
} else {
$data = array(
"Message" => "Unexpected status value",
"Status" => "error"
);
}
} else {
$data = array(
"Message" => "Recharge information not found",
"Status" => "error"
);
}
echo json_encode($data);
exit();
}
function VerifyHotspot() {
header('Content-Type: application/json');
$rawData = file_get_contents('php://input');
$postData = json_decode($rawData, true);
if (!$postData) {
echo json_encode(['Resultcode' => 'error', 'Message' => 'Invalid JSON data']);
return;
}
if (!isset($postData['account_id'])) {
echo json_encode(['Resultcode' => 'error', 'Message' => 'Missing required fields']);
return;
}
$accountId = $postData['account_id'];
$user = ORM::for_table('tbl_payment_gateway')
->where('username', $accountId)
->order_by_desc('id')
->find_one();
if ($user) {
$status = $user->status;
$mpesacode = $user->gateway_trx_id;
$res = $user->pg_paid_response;
if ($status == 2 && !empty($mpesacode)) {
echo json_encode([
"Resultcode" => "3",
"Message" => "We have received your transaction under the Mpesa Transaction $mpesacode. Please do not leave this page as we are redirecting you.",
"Status" => "success"
]);
} elseif ($res == "Not enough balance") {
echo json_encode([
"Resultcode" => "2",
"Message" => "Insufficient Balance for the transaction",
"Status" => "danger"
]);
} elseif ($res == "Wrong Mpesa pin") {
echo json_encode([
"Resultcode" => "2",
"Message" => "You entered Wrong Mpesa pin, please resubmit",
"Status" => "danger"
]);
} elseif ($status == 4) {
echo json_encode([
"Resultcode" => "2",
"Message" => "You cancelled the transaction, you can enter phone number again to activate",
"Status" => "info"
]);
} elseif (empty($mpesacode)) {
echo json_encode([
"Resultcode" => "1",
"Message" => "A payment pop up has been sent to your phone. Please enter PIN to continue (Please do not leave or reload the page until redirected).",
"Status" => "primary"
]);
}
} else {
echo json_encode([
"Resultcode" => "error",
"Message" => "User not found"
]);
}
}
function CreateHostspotUser()
{
header('Content-Type: application/json');
$rawData = file_get_contents('php://input');
$postData = json_decode($rawData, true);
if (!$postData) {
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'Invalid JSON DATA' . $postData . ' n tes ']);
} else {
$phone = $postData['phone_number'];
$planId = $postData['plan_id'];
$routerId = $postData['router_id'];
$accountId = $postData['account_id'];
if (!isset( $postData['phone_number'], $postData['plan_id'], $postData['router_id'], $postData['account_id'])) {
echo json_encode(['status' => 'error', 'code' => 400, 'message' => 'missing required fields' . $postData, 'phone' => $phone, 'planId' => $planId, 'routerId' => $routerId, 'accountId' => $accountId]);
} else {
$phone = (substr($phone, 0, 1) == '+') ? str_replace('+', '', $phone) : $phone;
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^0/', '254', $phone) : $phone;
$phone = (substr($phone, 0, 1) == '7') ? preg_replace('/^7/', '2547', $phone) : $phone; //cater for phone number prefix 2547XXXX
$phone = (substr($phone, 0, 1) == '1') ? preg_replace('/^1/', '2541', $phone) : $phone; //cater for phone number prefix 2541XXXX
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^01/', '2541', $phone) : $phone;
$phone = (substr($phone, 0, 1) == '0') ? preg_replace('/^07/', '2547', $phone) : $phone;
if (strlen($phone) !== 12) {
echo json_encode(['status' => 'error', 'code' => 1, 'message' => 'Phone number ' . $phone . ' is invalid. Please confirm.']);
}
if (strlen($phone) == 12 && !empty($planId) && !empty($routerId)) {
$PlanExist = ORM::for_table('tbl_plans')->where('id', $planId)->count() > 0;
$RouterExist = ORM::for_table('tbl_routers')->where('id', $routerId)->count() > 0;
if (!$PlanExist || !$RouterExist)
echo json_encode(["status" => "error", "message" => "Unable to process your request, please refresh the page."]);
}
$Userexist = ORM::for_table('tbl_customers')->where('username', $accountId)->find_one();
if ($Userexist) {
$Userexist->router_id = $routerId;
$Userexist->save();
InitiateStkpush($phone, $planId, $accountId, $routerId);
} else {
try {
$defpass = '1234';
$defaddr = 'netXtreme';
$defmail = $phone . '@gmail.com';
$createUser = ORM::for_table('tbl_customers')->create();
$createUser->username = $accountId;
$createUser->password = $defpass;
$createUser->fullname = $phone;
$createUser->router_id = $routerId;
$createUser->phonenumber = $phone;
$createUser->pppoe_password = $defpass;
$createUser->address = $defaddr;
$createUser->email = $defmail;
$createUser->service_type = 'Hotspot';
if ($createUser->save()) {
InitiateStkpush($phone, $planId, $accountId, $routerId);
} else {
echo json_encode(["status" => "error", "message" => "There was a system error when registering user, please contact support."]);
}
} catch (Exception $e) {
echo json_encode(["status" => "error", "message" => "Error creating user: " . $e->getMessage()]);
}
}
}
}
}
function InitiateStkpush($phone, $planId, $accountId, $routerId)
{
$gateway = ORM::for_table('tbl_appconfig')
->where('setting', 'payment_gateway')
->find_one();
$gateway = ($gateway) ? $gateway->value : null;
if ($gateway == "MpesatillStk") {
$url = U . "plugin/initiatetillstk";
} elseif ($gateway == "BankStkPush") {
$url = U . "plugin/initiatebankstk";
} elseif ($gateway == "mpesa") {
$url = U . "plugin/initiatempesa";
} else {
$url = null; // or handle the default case appropriately
}
$Planname = ORM::for_table('tbl_plans')
->where('id', $planId)
->order_by_desc('id')
->find_one();
$Findrouter = ORM::for_table('tbl_routers')
->where('id', $routerId)
->order_by_desc('id')
->find_one();
$rname = $Findrouter->name;
$price = $Planname->price;
$Planname = $Planname->name_plan;
$Checkorders = ORM::for_table('tbl_payment_gateway')
->where('username', $accountId)
->where('status', 1)
->order_by_desc('id')
->find_many();
if ($Checkorders) {
foreach ($Checkorders as $Dorder) {
$Dorder->delete();
}
}
try {
$d = ORM::for_table('tbl_payment_gateway')->create();
$d->username = $accountId;
$d->gateway = $gateway;
$d->plan_id = $planId;
$d->plan_name = $Planname;
$d->routers_id = $routerId;
$d->routers = $rname;
$d->price = $price;
$d->payment_method = $gateway;
$d->payment_channel = $gateway;
$d->created_date = date('Y-m-d H:i:s');
$d->paid_date = date('Y-m-d H:i:s');
$d->expired_date = date('Y-m-d H:i:s');
$d->pg_url_payment = $url;
$d->status = 1;
$d->save();
} catch (Exception $e) {
error_log('Error saving payment gateway record: ' . $e->getMessage());
throw $e;
}
SendSTKcred($phone, $url, $accountId);
}
function SendSTKcred($phone, $url, $accountId )
{
$link = $url;
$fields = array(
'username' => $accountId,
'phone' => $phone,
'channel' => 'Yes',
);
$postvars = http_build_query($fields);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $link);
curl_setopt($ch, CURLOPT_POST, count($fields));
curl_setopt($ch, CURLOPT_POSTFIELDS, $postvars);
$result = curl_exec($ch);
}
Alloworigins();

636
system/plugin/c2b.php Normal file
View File

@ -0,0 +1,636 @@
<?php
register_menu("Mpesa C2B Settings", true, "c2b_settings", 'SETTINGS', '', '', "");
register_menu("Mpesa Transactions", true, "c2b_overview", 'AFTER_MESSAGE', 'fa fa-paypal', '', "");
try {
$db = ORM::get_db();
$tableCheckQuery = "CREATE TABLE IF NOT EXISTS tbl_mpesa_transactions (
id INT AUTO_INCREMENT PRIMARY KEY,
TransID VARCHAR(255) NOT NULL,
TransactionType VARCHAR(255) NOT NULL,
TransTime VARCHAR(255) NOT NULL,
TransAmount DECIMAL(10, 2) NOT NULL,
BusinessShortCode VARCHAR(255) NOT NULL,
BillRefNumber VARCHAR(255) NOT NULL,
OrgAccountBalance DECIMAL(10, 2) NOT NULL,
MSISDN VARCHAR(255) NOT NULL,
FirstName VARCHAR(255) NOT NULL,
CustomerID VARCHAR(255) NOT NULL,
PackageName VARCHAR(255) NOT NULL,
PackagePrice VARCHAR(255) NOT NULL,
TransactionStatus VARCHAR(255) NOT NULL,
CreatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)";
$db->exec($tableCheckQuery);
} catch (PDOException $e) {
echo "Error creating the table: " . $e->getMessage();
} catch (Exception $e) {
echo "An unexpected error occurred: " . $e->getMessage();
}
function c2b_overview()
{
global $ui, $config;
_admin();
$ui->assign('_title', 'Mpesa C2B Payment Overview');
$ui->assign('_system_menu', '');
$admin = Admin::_info();
$ui->assign('_admin', $admin);
// Check user type for access
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
exit;
}
$query = ORM::for_table('tbl_mpesa_transactions')->order_by_desc('TransTime');
$payments = $query->find_many();
if (
(empty($config['mpesa_c2b_consumer_key']) || empty($config['mpesa_c2b_consumer_secret']) || empty($config['mpesa_c2b_business_code']))
&& !$config['c2b_registered']
) {
$ui->assign('message', '<em>' . Lang::T("You haven't registered your validation and verification URLs. Please register URLs by clicking ") . ' <a href="' . APP_URL . '/index.php?_route=plugin/c2b_settings"> Register URL </a>' . '</em>');
}
$ui->assign('payments', $payments);
$ui->assign('xheader', '<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.5/css/jquery.dataTables.css">');
$ui->display('c2b_overview.tpl');
}
function c2b_settings()
{
global $ui, $admin, $config;
$ui->assign('_title', Lang::T("Mpesa C2B Settings [Offline Payment]"));
$ui->assign('_system_menu', 'settings');
$admin = Admin::_info();
$ui->assign('_admin', $admin);
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
if (_post('save') == 'save') {
$mpesa_c2b_consumer_key = _post('mpesa_c2b_consumer_key');
$mpesa_c2b_consumer_secret = _post('mpesa_c2b_consumer_secret');
$mpesa_c2b_business_code = _post('mpesa_c2b_business_code');
$mpesa_c2b_env = _post('mpesa_c2b_env');
$mpesa_c2b_api = _post('mpesa_c2b_api');
$mpesa_c2b_low_fee = _post('mpesa_c2b_low_fee') ? 1 : 0;
$mpesa_c2b_bill_ref = _post('mpesa_c2b_bill_ref');
$errors = [];
if (empty($mpesa_c2b_consumer_key)) {
$errors[] = Lang::T('Mpesa C2B Consumer Key is required.');
}
if (empty($mpesa_c2b_consumer_secret)) {
$errors[] = Lang::T('Mpesa C2B Consumer Secret is required.');
}
if (empty($mpesa_c2b_business_code)) {
$errors[] = Lang::T('Mpesa C2B Business Code is required.');
}
if (empty($mpesa_c2b_env)) {
$errors[] = Lang::T('Mpesa C2B Environment is required.');
}
if (empty($mpesa_c2b_api)) {
$errors[] = Lang::T('Mpesa C2B API URL is required.');
}
if (empty($mpesa_c2b_bill_ref)) {
$errors[] = Lang::T('Mpesa Bill Ref Number Type is required.');
}
if (!empty($errors)) {
$ui->assign('message', implode('<br>', $errors));
$ui->display('c2b_settings.tpl');
return;
}
$settings = [
'mpesa_c2b_consumer_key' => $mpesa_c2b_consumer_key,
'mpesa_c2b_consumer_secret' => $mpesa_c2b_consumer_secret,
'mpesa_c2b_business_code' => $mpesa_c2b_business_code,
'mpesa_c2b_env' => $mpesa_c2b_env,
'mpesa_c2b_api' => $mpesa_c2b_api,
'mpesa_c2b_low_fee' => $mpesa_c2b_low_fee,
'mpesa_c2b_bill_ref' => $mpesa_c2b_bill_ref,
];
// Update or insert settings in the database
foreach ($settings as $key => $value) {
$d = ORM::for_table('tbl_appconfig')->where('setting', $key)->find_one();
if ($d) {
$d->value = $value;
$d->save();
} else {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = $key;
$d->value = $value;
$d->save();
}
}
if ($admin) {
_log('[' . $admin['username'] . ']: ' . Lang::T('Settings Saved Successfully'));
}
r2(U . 'plugin/c2b_settings', 's', Lang::T('Settings Saved Successfully'));
}
if (!empty($config['mpesa_c2b_consumer_key'] && $config['mpesa_c2b_consumer_secret'] && $config['mpesa_c2b_business_code']) && !$config['c2b_registered']) {
$ui->assign('message', '<em>' . Lang::T("You haven't registered your validation and verification URLs, Please register URLs by clicking ") . ' <a href="' . APP_URL . '/index.php?_route=plugin/c2b_settings"> Register URL </a>' . '</em>');
}
$ui->assign('_c', $config);
$ui->assign('companyName', $config['CompanyName']);
$ui->display('c2b_settings.tpl');
}
function c2b_generateAccessToken()
{
global $config;
$mpesa_c2b_env = $config['mpesa_c2b_env'] ?? null;
$mpesa_c2b_consumer_key = $config['mpesa_c2b_consumer_key'] ?? null;
$mpesa_c2b_consumer_secret = $config['mpesa_c2b_consumer_secret'] ?? null;
$access_token_url = match ($mpesa_c2b_env) {
"live" => 'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
"sandbox" => 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
};
$headers = ['Content-Type:application/json; charset=utf8'];
$curl = curl_init($access_token_url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_HEADER, FALSE);
curl_setopt($curl, CURLOPT_USERPWD, "$mpesa_c2b_consumer_key:$mpesa_c2b_consumer_secret");
$result = curl_exec($curl);
$result = json_decode($result);
if (isset($result->access_token)) {
return $result->access_token;
} else {
return null;
}
}
function c2b_registerUrl()
{
global $config;
if (
(empty($config['mpesa_c2b_consumer_key']) || empty($config['mpesa_c2b_consumer_secret']) || empty($config['mpesa_c2b_business_code']))
&& !$config['c2b_registered']
) {
r2(U . 'plugin/c2b_settings', 'e', Lang::T('Please setup your M-Pesa C2B settings first'));
exit;
}
$access_token = c2b_generateAccessToken();
switch ($access_token) {
case null:
r2(U . 'plugin/c2b_settings', 'e', Lang::T('Failed to generate access token'));
exit;
default:
$BusinessShortCode = $config['mpesa_c2b_business_code'] ?? null;
$mpesa_c2b_env = $config['mpesa_c2b_env'] ?? null;
$confirmationUrl = U . 'plugin/c2b_confirmation';
$validationUrl = U . 'plugin/c2b_validation';
$mpesa_c2b_api = $config['mpesa_c2b_api'] ?? null;
$registerurl = match ($mpesa_c2b_env) {
"live" => match ($mpesa_c2b_api) {
"v1" => 'https://api.safaricom.co.ke/mpesa/c2b/v1/registerurl',
"v2" => 'https://api.safaricom.co.ke/mpesa/c2b/v2/registerurl',
},
"sandbox" => 'https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl',
};
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $registerurl);
curl_setopt($curl, CURLOPT_HTTPHEADER, [
'Content-Type:application/json',
"Authorization:Bearer $access_token"
]);
$data = [
'ShortCode' => $BusinessShortCode,
'ResponseType' => 'Completed',
'ConfirmationURL' => $confirmationUrl,
'ValidationURL' => $validationUrl
];
$data_string = json_encode($data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
$curl_response = curl_exec($curl);
$data = json_decode($curl_response);
if (isset($data->ResponseCode) && $data->ResponseCode == 0) {
try {
$d = ORM::for_table('tbl_appconfig')->create();
$d->setting = 'c2b_registered';
$d->value = '1';
$d->save();
} catch (Exception $e) {
_log("Failed to save M-Pesa C2B URL to database.\n\n" . $e->getMessage());
sendTelegram("Failed to save M-Pesa C2B URL to database.\n\n" . $e->getMessage());
}
sendTelegram("M-Pesa C2B URL registered successfully");
r2(U . 'plugin/c2b_settings', 's', "M-Pesa C2B URL registered successfully");
} else {
$errorMessage = $data->errorMessage;
sendTelegram("Resister M-Pesa C2B URL Failed\n\n" . json_encode($curl_response, JSON_PRETTY_PRINT));
r2(U . 'plugin/c2b_settings', 'e', "Failed to register M-Pesa C2B URL Error $errorMessage");
}
break;
}
}
function c2b_webhook_log($data)
{
$logFile = 'pages/mpesa-webhook.html';
$logEntry = date('Y-m-d H:i:s') . "<pre>" . htmlspecialchars($data, ENT_QUOTES, 'UTF-8') . "</pre>\n";
if (file_put_contents($logFile, $logEntry, FILE_APPEND) === false) {
sendTelegram("Failed to write to log file: $logFile");
}
}
function c2b_isValidSafaricomIP($ip)
{
$config = c2b_config();
$safaricomIPs = [
'196.201.214.0/24',
'196.201.213.0/24',
'196.201.212.0/24',
'172.69.79.0/24',
'172.69.0.0/24',
'0.0.0.0/0',
];
if ($config['mpesa_c2b_env'] == 'sandbox') {
$safaricomIPs[] = '::1';
}
foreach ($safaricomIPs as $range) {
if (c2b_ipInRange($ip, $range)) {
return true;
}
}
return false;
}
function c2b_ipInRange($ip, $range)
{
list($subnet, $bits) = explode('/', $range);
$ip = ip2long($ip);
$subnet = ip2long($subnet);
$mask = -1 << (32 - $bits);
$subnet &= $mask;
return ($ip & $mask) == $subnet;
}
function c2b_confirmation()
{
global $config;
header("Content-Type: application/json");
$clientIP = $_SERVER['REMOTE_ADDR'];
if (!c2b_isValidSafaricomIP($clientIP)) {
c2b_logAndNotify("Unauthorized request from IP: {$clientIP}");
http_response_code(403);
echo json_encode(["ResultCode" => 1, "ResultDesc" => "Unauthorized"]);
return;
}
$mpesaResponse = file_get_contents('php://input');
if ($mpesaResponse === false) {
c2b_logAndNotify("Failed to get input stream.");
return;
}
c2b_webhook_log('Received webhook request');
c2b_webhook_log($mpesaResponse);
$content = json_decode($mpesaResponse);
if (json_last_error() !== JSON_ERROR_NONE) {
c2b_logAndNotify("Failed to decode JSON response: " . json_last_error_msg());
return;
}
c2b_webhook_log('Decoded JSON data successfully');
if (!class_exists('Package')) {
c2b_logAndNotify("Error: Package class does not exist.");
return;
}
if (isset($config['mpesa_c2b_bill_ref'])) {
switch ($config['mpesa_c2b_bill_ref']) {
case 'phone':
$customer = ORM::for_table('tbl_customers')
->where('phonenumber', $content->BillRefNumber)
->find_one();
break;
case 'username':
$customer = ORM::for_table('tbl_customers')
->where('username', $content->BillRefNumber)
->find_one();
break;
case 'id':
$customer = ORM::for_table('tbl_customers')
->where('id', $content->BillRefNumber)
->find_one();
break;
default:
$customer = null;
break;
}
if (!$customer) {
sendTelegram("Validation failed: No account found for BillRefNumber: $content->BillRefNumber");
_log("Validation failed: No account found for BillRefNumber: $content->BillRefNumber");
echo json_encode(["ResultCode" => "C2B00012", "ResultDesc" => "Invalid Account Number"]);
return;
}
} else {
_log("Configuration error: mpesa_c2b_bill_ref not set.");
sendTelegram("Configuration error: mpesa_c2b_bill_ref not set.");
}
$bills = c2b_billing($customer->id);
if (!$bills) {
c2b_logAndNotify("No matching bill found for BillRefNumber: {$content->BillRefNumber}");
return;
}
foreach ($bills as $bill) {
c2b_handleBillPayment($content, $customer, $bill);
}
echo json_encode(["ResultCode" => 0, "ResultDesc" => "Accepted"]);
}
function c2b_handleBillPayment($content, $customer, $bill)
{
$amountToPay = $bill['price'];
$amountPaid = $content->TransAmount;
$channel_mode = "Mpesa C2B - {$content->TransID}";
$customerBalance = $customer->balance;
$currentBalance = $customerBalance + $amountPaid;
$customerID = $customer->id;
try {
$transaction = c2b_storeTransaction($content, $bill['namebp'], $amountToPay, $customerID);
} catch (Exception $e) {
c2b_handleException("Failed to save transaction", $e);
exit;
}
if ($currentBalance >= $amountToPay) {
$excessAmount = $currentBalance - $amountToPay;
try {
$result = Package::rechargeUser($customer->id, $bill['routers'], $bill['plan_id'], 'mpesa', $channel_mode);
if (!$result) {
c2b_logAndNotify("Mpesa Payment Successful, but failed to activate the package for customer {$customer->username}.");
} else {
if ($excessAmount > 0) {
$customer->balance = $excessAmount;
$customer->save();
} else {
$customer->balance = 0;
$customer->save();
}
c2b_sendPaymentSuccessMessage($customer, $amountPaid, $bill['namebp']);
$transaction->transactionStatus = 'Completed';
$transaction->save();
}
} catch (Exception $e) {
c2b_handleException("Error during package activation", $e);
}
} else {
c2b_updateCustomerBalance($customer, $currentBalance, $amountPaid);
$neededToActivate = $amountToPay - $currentBalance;
c2b_sendBalanceUpdateMessage($customer, $amountPaid, $currentBalance, $neededToActivate);
$transaction->transactionStatus = 'Completed';
$transaction->save();
}
}
function c2b_storeTransaction($content, $packageName, $packagePrice, $customerID)
{
ORM::get_db()->beginTransaction();
try {
$transaction = ORM::for_table('tbl_mpesa_transactions')
->where('TransID', $content->TransID)
->find_one();
if ($transaction) {
// Update existing transaction
$transaction->TransactionType = $content->TransactionType;
$transaction->TransTime = $content->TransTime;
$transaction->TransAmount = $content->TransAmount;
$transaction->BusinessShortCode = $content->BusinessShortCode;
$transaction->BillRefNumber = $content->BillRefNumber;
$transaction->OrgAccountBalance = $content->OrgAccountBalance;
$transaction->MSISDN = $content->MSISDN;
$transaction->FirstName = $content->FirstName;
$transaction->PackageName = $packageName;
$transaction->PackagePrice = $packagePrice;
$transaction->customerID = $customerID;
$transaction->transactionStatus = 'Pending';
} else {
// Create new transaction
$transaction = ORM::for_table('tbl_mpesa_transactions')->create();
$transaction->TransID = $content->TransID;
$transaction->TransactionType = $content->TransactionType;
$transaction->TransTime = $content->TransTime;
$transaction->TransAmount = $content->TransAmount;
$transaction->BusinessShortCode = $content->BusinessShortCode;
$transaction->BillRefNumber = $content->BillRefNumber;
$transaction->OrgAccountBalance = $content->OrgAccountBalance;
$transaction->MSISDN = $content->MSISDN;
$transaction->FirstName = $content->FirstName;
$transaction->PackageName = $packageName;
$transaction->PackagePrice = $packagePrice;
$transaction->customerID = $customerID;
$transaction->transactionStatus = 'Pending';
}
$transaction->save();
ORM::get_db()->commit();
return $transaction;
} catch (Exception $e) {
ORM::get_db()->rollBack();
throw $e;
}
}
function c2b_logAndNotify($message)
{
_log($message);
sendTelegram($message);
}
function c2b_handleException($message, $e)
{
$fullMessage = "$message: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine();
c2b_logAndNotify($fullMessage);
}
function c2b_updateCustomerBalance($customer, $newBalance, $amountPaid)
{
try {
$customer->balance = $newBalance;
$customer->save();
c2b_logAndNotify("Payment of KES {$amountPaid} has been added to the balance of customer {$customer->username}.");
} catch (Exception $e) {
c2b_handleException("Failed to update customer balance", $e);
}
}
function c2b_sendPaymentSuccessMessage($customer, $amountPaid, $packageName)
{
$config = c2b_config();
$message = "Dear {$customer->fullname}, your payment of KES {$amountPaid} has been received and your plan {$packageName} has been successfully activated. Thank you for choosing {$config['CompanyName']}.";
c2b_sendNotification($customer, $message);
}
function c2b_sendBalanceUpdateMessage($customer, $amountPaid, $currentBalance, $neededToActivate)
{
$config = c2b_config();
$message = "Dear {$customer->fullname}, your payment of KES {$amountPaid} has been received and added to your account balance. Your current balance is KES {$currentBalance}.";
if ($neededToActivate > 0) {
$message .= " To activate your package, you need to add KES {$neededToActivate} more to your account.";
} else {
$message .= " Your current balance is sufficient to activate your package.";
}
$message .= "\n" . $config['CompanyName'];
c2b_sendNotification($customer, $message);
}
function c2b_sendNotification($customer, $message)
{
try {
Message::sendSMS($customer->phonenumber, $message);
Message::sendWhatsapp($customer->phonenumber, $message);
} catch (Exception $e) {
c2b_handleException("Failed to send SMS/WhatsApp message", $e);
}
}
function c2b_validation()
{
header("Content-Type: application/json");
$mpesaResponse = file_get_contents('php://input');
$config = c2b_config();
$content = json_decode($mpesaResponse);
if (json_last_error() !== JSON_ERROR_NONE) {
sendTelegram("Failed to decode JSON response.");
_log("Failed to decode JSON response.");
echo json_encode(["ResultCode" => "C2B00016", "ResultDesc" => "Invalid JSON format"]);
return;
}
$BillRefNumber = $content->BillRefNumber;
$TransAmount = $content->TransAmount;
if (isset($config['mpesa_c2b_bill_ref'])) {
switch ($config['mpesa_c2b_bill_ref']) {
case 'phone':
$customer = ORM::for_table('tbl_customers')
->where('phonenumber', $content->BillRefNumber)
->find_one();
break;
case 'username':
$customer = ORM::for_table('tbl_customers')
->where('username', $content->BillRefNumber)
->find_one();
break;
case 'id':
$customer = ORM::for_table('tbl_customers')
->where('id', $content->BillRefNumber)
->find_one();
break;
default:
$customer = null;
break;
}
if (!$customer) {
sendTelegram("Validation failed: No account found for BillRefNumber: $BillRefNumber");
_log("Validation failed: No account found for BillRefNumber: $BillRefNumber");
echo json_encode(["ResultCode" => "C2B00012", "ResultDesc" => "Invalid Account Number"]);
return;
}
} else {
_log("Configuration error: mpesa_c2b_bill_ref not set.");
sendTelegram("Configuration error: mpesa_c2b_bill_ref not set.");
}
$bills = c2b_billing($customer->id);
if (!$bills) {
sendTelegram("Validation failed: No bill found for BillRefNumber: $BillRefNumber");
_log("Validation failed: No bill found for BillRefNumber: $BillRefNumber");
echo json_encode(["ResultCode" => "C2B00012", "ResultDesc" => "Invalid Bill Reference"]);
return;
}
foreach ($bills as $bill) {
}
$billAmount = $bill['price'];
if (!$config['mpesa_c2b_low_fee']) {
if ($TransAmount < $billAmount) {
sendTelegram("Validation failed: Insufficient amount. Transferred: $TransAmount, Required: $billAmount");
_log("Validation failed: Insufficient amount. Transferred: $TransAmount, Required: $billAmount");
echo json_encode(["ResultCode" => "C2B00013", "ResultDesc" => "Invalid or Insufficient Amount"]);
return;
}
}
sendTelegram("Validation successful for BillRefNumber: $BillRefNumber with amount: $TransAmount");
_log("Validation successful for BillRefNumber: $BillRefNumber with amount: $TransAmount");
echo json_encode(["ResultCode" => 0, "ResultDesc" => "Accepted"]);
}
function c2b_billing($id)
{
$d = ORM::for_table('tbl_user_recharges')
->selects([
'customer_id',
'username',
'plan_id',
'namebp',
'recharged_on',
'recharged_time',
'expiration',
'time',
'status',
'method',
'plan_type',
['tbl_user_recharges.routers', 'routers'],
['tbl_user_recharges.type', 'type'],
'admin_id',
'prepaid'
])
->select('tbl_plans.price', 'price')
->left_outer_join('tbl_plans', array('tbl_plans.id', '=', 'tbl_user_recharges.plan_id'))
->where('customer_id', $id)
->find_many();
return $d;
}
function c2b_config()
{
$result = ORM::for_table('tbl_appconfig')->find_many();
foreach ($result as $value) {
$config[$value['setting']] = $value['value'];
}
return $config;
}

View File

@ -0,0 +1,48 @@
<?php
register_menu("Clear System Cache", true, "clear_cache", 'SETTINGS', '');
function clear_cache()
{
global $ui;
_admin();
$ui->assign('_title', 'Clear Cache');
$ui->assign('_system_menu', 'settings');
$admin = Admin::_info();
$ui->assign('_admin', $admin);
// Check user type for access
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
exit;
}
$compiledCacheDir = 'ui/compiled';
$templateCacheDir = 'system/cache';
try {
// Clear the compiled cache
$files = scandir($compiledCacheDir);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..' && is_file($compiledCacheDir . '/' . $file)) {
unlink($compiledCacheDir . '/' . $file);
}
}
// Clear the template cache
$templateCacheFiles = glob($templateCacheDir . '/*.{json,temp}', GLOB_BRACE);
foreach ($templateCacheFiles as $file) {
if (is_file($file)) {
unlink($file);
}
}
// Cache cleared successfully
_log('[' . ($admin['fullname'] ?? 'Unknown Admin') . ']: ' . Lang::T(' Cleared the system cache '), $admin['user_type']);
r2(U . 'dashboard', 's', Lang::T("Cache cleared successfully!"));
} catch (Exception $e) {
// Error occurred while clearing the cache
_log('[' . ($admin['fullname'] ?? 'Unknown Admin') . ']: ' . Lang::T(' Error occurred while clearing the cache: ' . $e->getMessage()), $admin['user_type']);
r2(U . 'dashboard', 'e', Lang::T("Error occurred while clearing the cache: ") . $e->getMessage());
}
}