Initial commit
This commit is contained in:
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# IDE/Editor specific files
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.vssscc
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Dependency Manager (Composer)
|
||||||
|
/vendor/
|
||||||
|
composer.phar
|
||||||
|
|
||||||
|
# Environment variables and sensitive files
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.php
|
||||||
|
secrets.php
|
||||||
|
|
||||||
|
# Temporary files and logs
|
||||||
|
/tmp/
|
||||||
|
/log/
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.cache
|
||||||
|
*.session
|
||||||
|
|
||||||
|
# Built assets (if applicable)
|
||||||
|
/public/build/
|
||||||
|
/public/storage/
|
||||||
|
|
||||||
|
# PHP-specific ignores
|
||||||
|
/web/cache/
|
||||||
|
*.swp
|
||||||
11
api-testing.md
Normal file
11
api-testing.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
https://payments.click-mobile.com/mse/api/public/disbursement
|
||||||
|
|
||||||
|
{
|
||||||
|
"transaction_id" : "MP1234567906",
|
||||||
|
"reference_id" : "INF99887755",
|
||||||
|
"mobile_number" : "888123456",
|
||||||
|
"amount" : "500"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
API Key : bd8f75cc-0f63-4707-9a2e-361800a0d94c
|
||||||
10
app/Config/database.php
Normal file
10
app/Config/database.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'host' => $_ENV['DB_HOST'],
|
||||||
|
'db' => $_ENV['DB_NAME'],
|
||||||
|
'user' => $_ENV['DB_USER'],
|
||||||
|
'pass' => $_ENV['DB_PASS'],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
34
app/Controllers/AuthController.php
Normal file
34
app/Controllers/AuthController.php
Normal 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']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
397
app/Controllers/DisbursementController.php
Normal file
397
app/Controllers/DisbursementController.php
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
14
app/Controllers/HomeController.php
Normal file
14
app/Controllers/HomeController.php
Normal 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']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
41
app/Controllers/UserController.php
Normal file
41
app/Controllers/UserController.php
Normal 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']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
37
app/Core/Auth.php
Normal file
37
app/Core/Auth.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class Auth {
|
||||||
|
public static function login($user) {
|
||||||
|
session_start();
|
||||||
|
session_regenerate_id(true); // Prevents session hijacking
|
||||||
|
$_SESSION['user_id'] = $user['id'];
|
||||||
|
$_SESSION['user_name'] = $user['username'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function check() {
|
||||||
|
if (session_status() === PHP_SESSION_NONE) session_start();
|
||||||
|
return isset($_SESSION['user_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function user() {
|
||||||
|
return $_SESSION['user_name'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function logout() {
|
||||||
|
session_start();
|
||||||
|
session_destroy();
|
||||||
|
header('Location: /login');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
public static function getBearerToken(): ?string {
|
||||||
|
$headers = $_SERVER['Authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? null;
|
||||||
|
if (!$headers && function_exists('apache_request_headers')) {
|
||||||
|
$req = apache_request_headers();
|
||||||
|
$headers = $req['Authorization'] ?? $req['authorization'] ?? null;
|
||||||
|
}
|
||||||
|
return ($headers && preg_match('/Bearer\s(\S+)/', $headers, $matches)) ? $matches[1] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
51
app/Core/Config.php
Normal file
51
app/Core/Config.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
private static $instance = null;
|
||||||
|
private array $settings = [];
|
||||||
|
|
||||||
|
private function __construct() {
|
||||||
|
// Map environment variables to internal keys with optional defaults
|
||||||
|
$this->settings = [
|
||||||
|
'db' => [
|
||||||
|
'host' => $_ENV['DB_HOST'] ?? 'localhost',
|
||||||
|
'name' => $_ENV['DB_NAME'] ?? 'my_app',
|
||||||
|
'user' => $_ENV['DB_USER'] ?? 'root',
|
||||||
|
'pass' => $_ENV['DB_PASS'] ?? '',
|
||||||
|
],
|
||||||
|
'app' => [
|
||||||
|
'name' => $_ENV['APP_NAME'] ?? 'Vanilla PHP App',
|
||||||
|
'env' => $_ENV['APP_ENV'] ?? 'production',
|
||||||
|
'debug' => ($_ENV['APP_DEBUG'] ?? 'false') === 'true',
|
||||||
|
'url' => $_ENV['APP_URL'] ?? 'http://localhost',
|
||||||
|
],
|
||||||
|
'mail' => [
|
||||||
|
'host' => $_ENV['MAIL_HOST'] ?? 'smtp.mailtrap.io',
|
||||||
|
'port' => $_ENV['MAIL_PORT'] ?? 2525,
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get(string $key, $default = null) {
|
||||||
|
if (self::$instance === null) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support dot notation (e.g., 'db.host')
|
||||||
|
$keys = explode('.', $key);
|
||||||
|
$value = self::$instance->settings;
|
||||||
|
|
||||||
|
foreach ($keys as $k) {
|
||||||
|
if (!isset($value[$k])) {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
$value = $value[$k];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
87
app/Core/Controller.php
Normal file
87
app/Core/Controller.php
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
abstract class Controller {
|
||||||
|
/**
|
||||||
|
* Helper to render a view file with data
|
||||||
|
*/
|
||||||
|
protected function renderOld(string $view, array $data = []) {
|
||||||
|
// Find the view file in the app/Views folder
|
||||||
|
$viewFile = __DIR__ . "/../Views/{$view}.phtml";
|
||||||
|
|
||||||
|
if (!file_exists($viewFile)) {
|
||||||
|
die("View file $view not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract data array into local variables for the view
|
||||||
|
extract($data);
|
||||||
|
|
||||||
|
// Start output buffering to capture the template content
|
||||||
|
ob_start();
|
||||||
|
include $viewFile;
|
||||||
|
echo ob_get_clean();
|
||||||
|
}
|
||||||
|
protected function render(string $view, array $data = []) {
|
||||||
|
extract($data);
|
||||||
|
|
||||||
|
// 1. Render the specific page view into a variable
|
||||||
|
ob_start();
|
||||||
|
include __DIR__ . "/../Views/{$view}.phtml";
|
||||||
|
$content = ob_get_clean();
|
||||||
|
|
||||||
|
// 2. Render the main layout and pass the $content into it
|
||||||
|
include __DIR__ . "/../Views/layouts/main.phtml";
|
||||||
|
}
|
||||||
|
protected function json(array $data, int $code = 200) {
|
||||||
|
// Set header to JSON
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
// Set HTTP status code (200, 201, 400, 404, etc.)
|
||||||
|
http_response_code($code);
|
||||||
|
|
||||||
|
// Output the data
|
||||||
|
echo json_encode($data);
|
||||||
|
exit; // End execution to prevent accidental HTML output
|
||||||
|
}
|
||||||
|
protected function getNetwork($msisdn){
|
||||||
|
$phone = str_replace(' ', '', $msisdn);
|
||||||
|
$phone = str_replace('+', '', $msisdn);
|
||||||
|
$pattern = "/^\+?(265|0)?[89]\d{8}$/i";
|
||||||
|
|
||||||
|
$retval = preg_match($pattern, $phone, $matches, PREG_OFFSET_CAPTURE);
|
||||||
|
if (count($matches) < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
elseif (strlen($phone) == 9) {
|
||||||
|
$msisdn = "265" . $phone;
|
||||||
|
}
|
||||||
|
elseif (strlen($phone) == 10) {
|
||||||
|
$phone = ltrim($phone, 0);
|
||||||
|
$msisdn = "265" . $phone;
|
||||||
|
}
|
||||||
|
elseif (strlen($phone) == 12) {
|
||||||
|
$msisdn = $phone;
|
||||||
|
}
|
||||||
|
if (!isset($msisdn)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$prefix = substr($msisdn, 0, 4);
|
||||||
|
|
||||||
|
if ($prefix == '2658') {
|
||||||
|
$network = 'tnm';
|
||||||
|
}
|
||||||
|
elseif ($prefix == '2659' ) {
|
||||||
|
$network = 'airtel';
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return $network;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
17
app/Core/Csrf.php
Normal file
17
app/Core/Csrf.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class Csrf {
|
||||||
|
public static function generate() {
|
||||||
|
Session::start();
|
||||||
|
if (empty($_SESSION['csrf_token'])) {
|
||||||
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||||
|
}
|
||||||
|
return $_SESSION['csrf_token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function verify($token) {
|
||||||
|
Session::start();
|
||||||
|
return hash_equals($_SESSION['csrf_token'] ?? '', $token);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Core/Database.php
Normal file
33
app/Core/Database.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class Database {
|
||||||
|
private static $instance = null;
|
||||||
|
private $connection;
|
||||||
|
|
||||||
|
private function __construct() {
|
||||||
|
$config = require __DIR__ . '/../Config/database.php';
|
||||||
|
|
||||||
|
$dsn = "mysql:host={$config['host']};dbname={$config['db']};charset=utf8mb4";
|
||||||
|
$this->connection = new PDO($dsn, $config['user'], $config['pass'], [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getInstance() {
|
||||||
|
if (!self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance->connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage in a Model:
|
||||||
|
// $db = \App\Core\Database::getInstance();
|
||||||
|
// $stmt = $db->query("SELECT * FROM users");
|
||||||
|
|
||||||
|
?>
|
||||||
60
app/Core/Logger.php
Normal file
60
app/Core/Logger.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
private string $logFile;
|
||||||
|
private int $maxSize = 5242880;
|
||||||
|
private int $maxFiles = 5;
|
||||||
|
|
||||||
|
public function __construct(string $filename = 'app.log') {
|
||||||
|
// Place logs in storage/logs/ at the project root
|
||||||
|
$this->logFile = __DIR__ . "/../../storage/logs/" . $filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function log(string $message, string $level = 'INFO'): void {
|
||||||
|
$this->checkRotation();
|
||||||
|
|
||||||
|
$timestamp = date('Y-m-d H:i:s');
|
||||||
|
$formattedMessage = "[$timestamp] [$level] $message" . PHP_EOL;
|
||||||
|
|
||||||
|
file_put_contents($this->logFile, $formattedMessage, FILE_APPEND | LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkRotation(): void {
|
||||||
|
if (file_exists($this->logFile) && filesize($this->logFile) >= $this->maxSize) {
|
||||||
|
$this->rotate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function rotate(): void {
|
||||||
|
// Delete the oldest file if it exists (e.g., app.log.5)
|
||||||
|
$oldestFile = $this->logFile . '.' . $this->maxFiles;
|
||||||
|
if (file_exists($oldestFile)) {
|
||||||
|
unlink($oldestFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift existing files up (4 becomes 5, 3 becomes 4, etc.)
|
||||||
|
for ($i = $this->maxFiles - 1; $i >= 1; $i--) {
|
||||||
|
$currentFile = $this->logFile . '.' . $i;
|
||||||
|
$nextFile = $this->logFile . '.' . ($i + 1);
|
||||||
|
if (file_exists($currentFile)) {
|
||||||
|
rename($currentFile, $nextFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the main log file to .1
|
||||||
|
rename($this->logFile, $this->logFile . '.1');
|
||||||
|
|
||||||
|
// Create a new empty log file
|
||||||
|
touch($this->logFile);
|
||||||
|
chmod($this->logFile, 0664);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand helpers
|
||||||
|
public function info($msg) { $this->log($msg, 'INFO'); }
|
||||||
|
public function error($msg) { $this->log($msg, 'ERROR'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
33
app/Core/Model.php
Normal file
33
app/Core/Model.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
abstract class Model {
|
||||||
|
protected static $table;
|
||||||
|
|
||||||
|
protected static function builder() {
|
||||||
|
$pdo = Database::getInstance();
|
||||||
|
return (new QueryBuilder($pdo))->table(static::$table);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function all() {
|
||||||
|
return self::builder()->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function find($id) {
|
||||||
|
return self::builder()->where('id', $id)->get()[0] ?? null;
|
||||||
|
}
|
||||||
|
public static function create(array $data) {
|
||||||
|
return static::builder()->insert($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function updateById($id, array $data) {
|
||||||
|
return static::builder()->where('id', $id)->update($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function deleteById($id) {
|
||||||
|
return static::builder()->where('id', $id)->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
71
app/Core/OldQueryBuilder.php
Normal file
71
app/Core/OldQueryBuilder.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class OldQueryBuilder {
|
||||||
|
protected $pdo;
|
||||||
|
protected $table;
|
||||||
|
protected $where = [];
|
||||||
|
protected $params = [];
|
||||||
|
|
||||||
|
public function __construct(PDO $pdo) {
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table($table) {
|
||||||
|
$this->table = $table;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function where($column, $value) {
|
||||||
|
$this->where[] = "{$column} = :{$column}";
|
||||||
|
$this->params[$column] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get() {
|
||||||
|
$sql = "SELECT * FROM {$this->table}";
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' AND ', $this->where);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare($sql);
|
||||||
|
$stmt->execute($this->params);
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
public function insert(array $data) {
|
||||||
|
$columns = implode(', ', array_keys($data));
|
||||||
|
$placeholders = ':' . implode(', :', array_keys($data));
|
||||||
|
|
||||||
|
$sql = "INSERT INTO {$this->table} ({$columns}) VALUES ({$placeholders})";
|
||||||
|
return $this->pdo->prepare($sql)->execute($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(array $data) {
|
||||||
|
$fields = "";
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$fields .= "{$key} = :{$key}, ";
|
||||||
|
}
|
||||||
|
$fields = rtrim($fields, ', ');
|
||||||
|
|
||||||
|
$sql = "UPDATE {$this->table} SET {$fields}";
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' AND ', $this->where);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge update data with where parameters
|
||||||
|
return $this->pdo->prepare($sql)->execute(array_merge($data, $this->params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete() {
|
||||||
|
$sql = "DELETE FROM {$this->table}";
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' AND ', $this->where);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pdo->prepare($sql)->execute($this->params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
82
app/Core/QueryBuilder copy.php
Normal file
82
app/Core/QueryBuilder copy.php
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
|
||||||
|
class QueryBuilder {
|
||||||
|
protected $pdo;
|
||||||
|
protected $table;
|
||||||
|
protected $where = [];
|
||||||
|
protected $params = [];
|
||||||
|
|
||||||
|
public function __construct(PDO $pdo) {
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table($table) {
|
||||||
|
$this->table = $table;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function where($column, $operator, $value = null) {
|
||||||
|
if ($value === null) {
|
||||||
|
$value = $operator;
|
||||||
|
$operator = '=';
|
||||||
|
}
|
||||||
|
$paramName = 'where_' . str_replace('.', '_', $column) . '_' . count($this->params);
|
||||||
|
|
||||||
|
$this->where[] = "{$column} {$operator} :{$paramName}";
|
||||||
|
$this->params[$paramName] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get() {
|
||||||
|
$sql = "SELECT * FROM {$this->table}";
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' AND ', $this->where);
|
||||||
|
}
|
||||||
|
|
||||||
|
// echo "<b>SQL:</b> " . $sql . "<br><br>";
|
||||||
|
// echo "<b>Params:</b><pre>"; print_r($this->params); echo "</pre>";
|
||||||
|
// // die();
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare($sql);
|
||||||
|
$stmt->execute($this->params);
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insert(array $data) {
|
||||||
|
$columns = implode(', ', array_keys($data));
|
||||||
|
$placeholders = ':' . implode(', :', array_keys($data));
|
||||||
|
|
||||||
|
$sql = "INSERT INTO {$this->table} ({$columns}) VALUES ({$placeholders})";
|
||||||
|
return $this->pdo->prepare($sql)->execute($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(array $data) {
|
||||||
|
$fields = "";
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$fields .= "{$key} = :{$key}, ";
|
||||||
|
}
|
||||||
|
$fields = rtrim($fields, ', ');
|
||||||
|
|
||||||
|
$sql = "UPDATE {$this->table} SET {$fields}";
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' AND ', $this->where);
|
||||||
|
}
|
||||||
|
return $this->pdo->prepare($sql)->execute(array_merge($data, $this->params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete() {
|
||||||
|
$sql = "DELETE FROM {$this->table}";
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' AND ', $this->where);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pdo->prepare($sql)->execute($this->params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
163
app/Core/QueryBuilder.php
Normal file
163
app/Core/QueryBuilder.php
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class QueryBuilder {
|
||||||
|
protected $pdo;
|
||||||
|
protected $table;
|
||||||
|
protected $where = [];
|
||||||
|
protected $params = [];
|
||||||
|
protected $orderBy = [];
|
||||||
|
protected $limit = null;
|
||||||
|
protected $selects = '*'; // Defaults to everything
|
||||||
|
protected $joins = [];
|
||||||
|
|
||||||
|
public function __construct(PDO $pdo) {
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table($table) {
|
||||||
|
$this->table = $table;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notice the new $boolean parameter (defaults to 'AND')
|
||||||
|
public function where($column, $operator, $value = null, $boolean = 'AND') {
|
||||||
|
if ($value === null) {
|
||||||
|
$value = $operator;
|
||||||
|
$operator = '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
$paramName = 'where_' . str_replace('.', '_', $column) . '_' . count($this->params);
|
||||||
|
|
||||||
|
// If this is the first where clause, we don't need 'AND' or 'OR'
|
||||||
|
$prefix = empty($this->where) ? '' : strtoupper($boolean) . ' ';
|
||||||
|
|
||||||
|
$this->where[] = "{$prefix}{$column} {$operator} :{$paramName}";
|
||||||
|
$this->params[$paramName] = $value;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// orWhere simply calls where() but passes 'OR'
|
||||||
|
public function orWhere($column, $operator, $value = null) {
|
||||||
|
return $this->where($column, $operator, $value, 'OR');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the results
|
||||||
|
public function orderBy($column, $direction = 'ASC') {
|
||||||
|
$direction = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
|
||||||
|
$this->orderBy[] = "{$column} {$direction}";
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit the number of results
|
||||||
|
public function limit(int $limit) {
|
||||||
|
$this->limit = $limit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch a single record instead of an array of arrays
|
||||||
|
public function first() {
|
||||||
|
$this->limit(1);
|
||||||
|
$result = $this->get();
|
||||||
|
return $result ? $result[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOld() {
|
||||||
|
$sql = "SELECT * FROM {$this->table}";
|
||||||
|
|
||||||
|
// Changed implode from ' AND ' to ' ' because the prefix handles it now
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' ', $this->where);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->orderBy)) {
|
||||||
|
$sql .= " ORDER BY " . implode(', ', $this->orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->limit !== null) {
|
||||||
|
$sql .= " LIMIT {$this->limit}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare($sql);
|
||||||
|
$stmt->execute($this->params);
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
public function get() {
|
||||||
|
// Use the new selects property instead of hardcoded '*'
|
||||||
|
$sql = "SELECT {$this->selects} FROM {$this->table}";
|
||||||
|
|
||||||
|
// Add joins immediately after the FROM clause
|
||||||
|
if (!empty($this->joins)) {
|
||||||
|
$sql .= " " . implode(' ', $this->joins);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' ', $this->where);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->orderBy)) {
|
||||||
|
$sql .= " ORDER BY " . implode(', ', $this->orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->limit !== null) {
|
||||||
|
$sql .= " LIMIT {$this->limit}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare($sql);
|
||||||
|
$stmt->execute($this->params);
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insert(array $data) {
|
||||||
|
$columns = implode(', ', array_keys($data));
|
||||||
|
$placeholders = ':' . implode(', :', array_keys($data));
|
||||||
|
|
||||||
|
$sql = "INSERT INTO {$this->table} ({$columns}) VALUES ({$placeholders})";
|
||||||
|
return $this->pdo->prepare($sql)->execute($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(array $data) {
|
||||||
|
$fields = "";
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$fields .= "{$key} = :{$key}, ";
|
||||||
|
}
|
||||||
|
$fields = rtrim($fields, ', ');
|
||||||
|
|
||||||
|
$sql = "UPDATE {$this->table} SET {$fields}";
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' ', $this->where);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pdo->prepare($sql)->execute(array_merge($data, $this->params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete() {
|
||||||
|
$sql = "DELETE FROM {$this->table}";
|
||||||
|
if (!empty($this->where)) {
|
||||||
|
$sql .= " WHERE " . implode(' ', $this->where);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->pdo->prepare($sql)->execute($this->params);
|
||||||
|
}
|
||||||
|
// Specify exactly which columns to return
|
||||||
|
public function select($columns) {
|
||||||
|
// Allow passing an array ['id', 'name'] or a raw string 'id, name'
|
||||||
|
$this->selects = is_array($columns) ? implode(', ', $columns) : $columns;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main join method
|
||||||
|
public function join($table, $firstColumn, $operator, $secondColumn, $type = 'INNER') {
|
||||||
|
$this->joins[] = "{$type} JOIN {$table} ON {$firstColumn} {$operator} {$secondColumn}";
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helpful shortcut for LEFT JOINS
|
||||||
|
public function leftJoin($table, $firstColumn, $operator, $secondColumn) {
|
||||||
|
return $this->join($table, $firstColumn, $operator, $secondColumn, 'LEFT');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
66
app/Core/Router.php
Normal file
66
app/Core/Router.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class Router {
|
||||||
|
protected $routes = [];
|
||||||
|
|
||||||
|
public function get($path, $callback) {
|
||||||
|
$this->routes['GET'][$path] = $callback;
|
||||||
|
}
|
||||||
|
// Registers POST routes
|
||||||
|
public function post($path, $callback) {
|
||||||
|
$this->routes['POST'][$path] = $callback;
|
||||||
|
}
|
||||||
|
public function resolve($uri, $method) {
|
||||||
|
// Strip query strings (e.g., /users?id=1 becomes /users)
|
||||||
|
// In public/index.php
|
||||||
|
|
||||||
|
$path = parse_url($uri, PHP_URL_PATH);
|
||||||
|
// var_dump($path);
|
||||||
|
$callback = $this->routes[$method][$path] ?? null;
|
||||||
|
|
||||||
|
if (!$callback) {
|
||||||
|
http_response_code(404);
|
||||||
|
// echo "404 Not Found";
|
||||||
|
$this->handleNotFound($path, $method);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle 'Controller@method' strings
|
||||||
|
if (is_string($callback)) {
|
||||||
|
[$controllerName, $methodName] = explode('@', $callback);
|
||||||
|
$controllerClass = "\\App\\Controllers\\" . $controllerName;
|
||||||
|
$controller = new $controllerClass();
|
||||||
|
$controller->$methodName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function handleNotFound($path, $method) {
|
||||||
|
$logger = new Logger();
|
||||||
|
$isDebug = ($_ENV['APP_DEBUG'] ?? 'false') === 'true';
|
||||||
|
|
||||||
|
// 1. Log the failure for the developer
|
||||||
|
$logMessage = "404 Not Found | Method: $method | URI: $path";
|
||||||
|
$logger->error($logMessage);
|
||||||
|
|
||||||
|
http_response_code(404);
|
||||||
|
|
||||||
|
// 2. If Debug is ON, show detailed info in the browser
|
||||||
|
if ($isDebug) {
|
||||||
|
// echo "<h1>404 Not Found (Debug Mode)</h1>";
|
||||||
|
// echo "<p><strong>Method:</strong> $method</p>";
|
||||||
|
// echo "<p><strong>Attempted URI:</strong> $path</p>";
|
||||||
|
// echo "<p><strong>Defined Routes:</strong></p><pre>";
|
||||||
|
// print_r($this->routes[$method] ?? []);
|
||||||
|
// echo "</pre>";
|
||||||
|
|
||||||
|
echo json_encode([ 'message' => 'Not Found', 'method' => $method, 'path' => $path]);
|
||||||
|
} else {
|
||||||
|
// Simple message for production
|
||||||
|
echo "404 Not Found";
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
22
app/Core/Session.php
Normal file
22
app/Core/Session.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class Session {
|
||||||
|
public static function start() {
|
||||||
|
if (session_status() === PHP_SESSION_NONE) session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function setFlash($key, $message) {
|
||||||
|
self::start();
|
||||||
|
$_SESSION['flash'][$key] = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getFlash($key) {
|
||||||
|
self::start();
|
||||||
|
$message = $_SESSION['flash'][$key] ?? null;
|
||||||
|
unset($_SESSION['flash'][$key]); // Delete after retrieval
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
24
app/Core/Validator.php
Normal file
24
app/Core/Validator.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
class Validator {
|
||||||
|
public static function validate(array $data, array $rules) {
|
||||||
|
$errors = [];
|
||||||
|
foreach ($rules as $field => $rule) {
|
||||||
|
if (strpos($rule, 'required') !== false && empty($data[$field])) {
|
||||||
|
$errors[$field] = ucfirst($field) . " is required.";
|
||||||
|
}
|
||||||
|
if (strpos($rule, 'email') !== false && !filter_var($data[$field], FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errors[$field] = "Invalid email format.";
|
||||||
|
}
|
||||||
|
if (strpos($rule, 'min:8') !== false && strlen($data[$field] ?? '') < 8) {
|
||||||
|
$errors[$field] = ucfirst($field) . " must be at least 8 characters.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
25
app/Models/AirtelBroker.php
Normal file
25
app/Models/AirtelBroker.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Core\Model;
|
||||||
|
|
||||||
|
class AirtelBroker extends Model {
|
||||||
|
protected static $table = 'airtel_money_wallets';
|
||||||
|
|
||||||
|
public static function findActive() {
|
||||||
|
return self::builder()->where('status', 'active')->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function getBroker($wallet_id){
|
||||||
|
|
||||||
|
$details = self::builder()->where('id', $wallet_id)->get();
|
||||||
|
if ($details == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
47
app/Models/Auth.php
Normal file
47
app/Models/Auth.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Auth {
|
||||||
|
|
||||||
|
private $connection;
|
||||||
|
// private $bearer_token;
|
||||||
|
|
||||||
|
public function __construct($db) {
|
||||||
|
$this->connection = $db;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function read_api_auth(){
|
||||||
|
// $bearer = $this->bearer_token;
|
||||||
|
|
||||||
|
if(!function_exists('getallheaders')){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$headers = [];
|
||||||
|
foreach (getallheaders() as $name => $value) {
|
||||||
|
// echo "$name: $value <br>" . PHP_EOL;
|
||||||
|
$headers[$name] = $value;
|
||||||
|
}
|
||||||
|
$check = array_key_exists('Authorization', $headers);
|
||||||
|
if ($check == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
list($type, $bearer_token) = explode(" ", $headers['Authorization'], 2);
|
||||||
|
|
||||||
|
$query = 'SELECT id, name FROM auth_users WHERE bearer_token = ? LIMIT 0,1';
|
||||||
|
$statement = $this->connection->prepare($query);
|
||||||
|
|
||||||
|
$statement->bindParam(1, $bearer_token);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$row = $statement->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($row == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
39
app/Models/Disbursement.php
Normal file
39
app/Models/Disbursement.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Core\Model;
|
||||||
|
|
||||||
|
class Disbursement extends Model {
|
||||||
|
protected static $table = 'disbursements';
|
||||||
|
|
||||||
|
public static function findActive() {
|
||||||
|
return self::builder()->where('status', 'active')->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function varifyTransaction($transaction_id, $reference_number){
|
||||||
|
|
||||||
|
$transaction = self::builder()->where('transaction_id', $transaction_id)
|
||||||
|
->where('infotech_transaction_id', $reference_number)
|
||||||
|
->get();
|
||||||
|
if ($transaction == false) {
|
||||||
|
// code...
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $transaction;
|
||||||
|
}
|
||||||
|
public static function updateTransaction($id, $params){
|
||||||
|
|
||||||
|
$transaction = self::builder()->where('transaction_id', $transaction_id)
|
||||||
|
->where('infotech_transaction_id', $reference_number)
|
||||||
|
->get();
|
||||||
|
if ($transaction == false) {
|
||||||
|
// code...
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
24
app/Models/MpambaBroker.php
Normal file
24
app/Models/MpambaBroker.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Core\Model;
|
||||||
|
|
||||||
|
class MpambaBroker extends Model {
|
||||||
|
protected static $table = 'mpamba_tnm_wallets';
|
||||||
|
|
||||||
|
public static function findActive() {
|
||||||
|
return self::builder()->where('status', 'active')->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function getBroker($wallet_id){
|
||||||
|
$details = self::builder()->where('wallet_id', $wallet_id)->get();
|
||||||
|
if ($details == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $details;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
20
app/Models/User.php
Normal file
20
app/Models/User.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Core\Model;
|
||||||
|
|
||||||
|
class User extends Model {
|
||||||
|
// Tell the model which database table to use
|
||||||
|
protected static $table = 'auth_users';
|
||||||
|
|
||||||
|
// You can add custom business logic here
|
||||||
|
public static function findActive() {
|
||||||
|
return self::builder()->where('status', 'active')->get();
|
||||||
|
}
|
||||||
|
public static function getByToken($token) {
|
||||||
|
return self::builder()->where('bearer_token', $token)->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
27
app/Views/layouts/main.phtml
Normal file
27
app/Views/layouts/main.phtml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title><?= $title ?? 'My PHP App' ?></title>
|
||||||
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<?php if (\App\Core\Auth::check()): ?>
|
||||||
|
<span>Welcome, <?= htmlspecialchars(\App\Core\Auth::user()) ?></span>
|
||||||
|
<a href="/logout">Logout</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="/login">Login</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<!-- This is where the page content goes -->
|
||||||
|
<?= $content ?>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>© <?= date('Y') ?> My Vanilla App</footer>
|
||||||
|
<script src="/js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
8
app/Views/users/index.phtml
Normal file
8
app/Views/users/index.phtml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<!-- The $title and $users variables were passed from the controller -->
|
||||||
|
<h1><?= htmlspecialchars($title) ?></h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($users as $user): ?>
|
||||||
|
<li><?= htmlspecialchars($user['name']) ?> (<?= htmlspecialchars($user['email']) ?>)</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
19
app/auth_user.php
Normal file
19
app/auth_user.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
// Headers
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
header('Access-Control-Allow-Methods: POST');
|
||||||
|
header('Access-Control-Allow-Headers: Access-Control-Allow-Headers,Content-Type,Access-Control-Allow-Methods, Authorization, X-Requested-With');
|
||||||
|
|
||||||
|
include_once '../models/Auth.php';
|
||||||
|
include_once '../config/Database.php';
|
||||||
|
//require_once("../getrequestheaders.php");
|
||||||
|
|
||||||
|
|
||||||
|
$database = new Database();
|
||||||
|
$db = $database->connect();
|
||||||
|
|
||||||
|
|
||||||
|
$auth = new Auth($db);
|
||||||
|
$retval = $auth->read_api_auth();
|
||||||
|
var_dump($retval);
|
||||||
69
app/disbursement.php
Normal file
69
app/disbursement.php
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
// Headersa
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
header('Access-Control-Allow-Methods: POST');
|
||||||
|
// header('Access-Control-Allow-Headers: Access-Control-Allow-Headers, Content-Type,Access-Control-Allow-Methods, Authorization, X-Requested-With');
|
||||||
|
|
||||||
|
include_once 'models/Auth.php';
|
||||||
|
include_once 'config/Database.php';
|
||||||
|
include_once 'models/Disbursement.php';
|
||||||
|
|
||||||
|
$database = new Database();
|
||||||
|
$db = $database->connect();
|
||||||
|
|
||||||
|
$auth = new Auth($db);
|
||||||
|
|
||||||
|
$retval = $auth->read_api_auth();
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents("php://input") , true);
|
||||||
|
|
||||||
|
@file_put_contents("logs/" . date("Y_m_d_") . "contact_centre_electricity_purchase_requests.txt", json_encode($data) . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
|
||||||
|
if ($retval == false) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(["status" => "fail", "message" => "Unauthorised Access"]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$requested_keys = ['transaction_id', 'reference_id', 'mobile_number', 'amount'];
|
||||||
|
$missing = [];
|
||||||
|
|
||||||
|
$missing = array_diff_key(array_flip($requested_keys), $data);
|
||||||
|
if (count($missing) > 0) {
|
||||||
|
$missing_string = implode(", ", array_flip($missing));
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["status" => "fail", "message" => "Required parameter(s) missing : $missing_string"]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// var_dump($missing);
|
||||||
|
// var_dump($data); die;
|
||||||
|
|
||||||
|
// TODO: check if transaction ID matches our record in DB
|
||||||
|
|
||||||
|
$result = varifyTransaction($data['transaction_id'], $data['mobile_number']);
|
||||||
|
// var_dump($result);
|
||||||
|
if ($result == false) {
|
||||||
|
http_response_code(200);
|
||||||
|
echo json_encode(["status" => "fail", "message" => "Transaction ID not found"]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($result !== false) {
|
||||||
|
// TODO: send request to ultima to purchase electricty
|
||||||
|
$result = processDisbursement($data['transaction_id'], $data['mobile_number'], $data['amount'], $data['reference_id']);
|
||||||
|
if ($electricity_token !== false) {
|
||||||
|
http_response_code(200);
|
||||||
|
echo json_encode(["status" => "success", "reference_id" => $data['reference_id']]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
http_response_code(200);
|
||||||
|
echo json_encode(["status" => "fail", "reference_id" => $data['reference_id'], "message" => "Disbursement could not be processed at this time"]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
http_response_code(200);
|
||||||
|
echo json_encode(["status" => "fail", "reference_id" => $data['reference_id'], "message" => "Disbursement failed"]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
88
app/functions/app_functions.php
Normal file
88
app/functions/app_functions.php
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
defined('SMS_API_KEY') OR define("SMS_API_KEY", "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI1NDciLCJvaWQiOjU0NywidWlkIjoiNmFmMmMyZDktZTIwZS00YmYwLTg4ZTgtOGEwMzY0YmU5YTAyIiwiYXBpZCI6MzM1LCJpYXQiOjE3MTIyMjcyNDcsImV4cCI6MjA1MjIyNzI0N30.CG5VW2FU18yx-1OUMtPqFMce06LFUZwai-ecdmb79Ls67T7k4L7RuinSluZMWn1epP883ongI-E5fklSBVaEvQ");
|
||||||
|
|
||||||
|
|
||||||
|
function httpPostNew($params){
|
||||||
|
|
||||||
|
$curl = curl_init();
|
||||||
|
curl_setopt_array($curl, array(
|
||||||
|
CURLOPT_URL => 'http://206.225.81.36:8989/api/messaging/sendsms',
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_ENCODING => '',
|
||||||
|
CURLOPT_MAXREDIRS => 10,
|
||||||
|
CURLOPT_TIMEOUT => 0,
|
||||||
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||||
|
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||||
|
CURLOPT_POSTFIELDS => $params,
|
||||||
|
CURLOPT_HTTPHEADER => array(
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'Authorization: Bearer ' . SMS_API_KEY
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
$response = curl_exec($curl);
|
||||||
|
|
||||||
|
curl_close($curl);
|
||||||
|
return $response;
|
||||||
|
|
||||||
|
}
|
||||||
|
function slackCurl($message){
|
||||||
|
$curl = curl_init();
|
||||||
|
curl_setopt_array($curl, array(
|
||||||
|
CURLOPT_URL => "https://hooks.slack.com/services/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",
|
||||||
|
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_ENCODING => "",
|
||||||
|
CURLOPT_MAXREDIRS => 10,
|
||||||
|
CURLOPT_TIMEOUT => 30,
|
||||||
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||||
|
CURLOPT_CUSTOMREQUEST => "POST",
|
||||||
|
CURLOPT_POSTFIELDS => $message,
|
||||||
|
CURLOPT_HTTPHEADER => array(
|
||||||
|
"cache-control: no-cache",
|
||||||
|
"content-type: application/json"
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
$response = curl_exec($curl);
|
||||||
|
$err = curl_error($curl);
|
||||||
|
|
||||||
|
curl_close($curl);
|
||||||
|
|
||||||
|
if ($err) {
|
||||||
|
return "cURL Error #:" . $err;
|
||||||
|
} else {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendSlack($message){
|
||||||
|
$retval = array('text' => $message);
|
||||||
|
$response = slackCurl(json_encode($retval));
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sendNtfy($data){
|
||||||
|
$curl = curl_init();
|
||||||
|
curl_setopt_array($curl, array(
|
||||||
|
CURLOPT_HTTPHEADER => array(
|
||||||
|
'Content-Type: application/json'
|
||||||
|
),
|
||||||
|
CURLOPT_URL => 'https://ntfy.sh/bpc_rest_api',
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_ENCODING => '',
|
||||||
|
CURLOPT_MAXREDIRS => 10,
|
||||||
|
CURLOPT_TIMEOUT => 0,
|
||||||
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||||
|
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||||
|
CURLOPT_POSTFIELDS => $data
|
||||||
|
));
|
||||||
|
$response = curl_exec($curl);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
17
check.php
Normal file
17
check.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if(strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') != 0){
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["status" => "fail", "message" => "Request method must be POST"]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
|
||||||
|
|
||||||
|
if(strcasecmp($contentType, 'application/json') != 0){
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["status" => "fail", "message" => "Content type must be: application/json"]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
14
composer.json
Normal file
14
composer.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "click-mobile/mse-api",
|
||||||
|
"description": "Api service for the main application",
|
||||||
|
"type": "project",
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.4",
|
||||||
|
"vlucas/phpdotenv": "^5.5"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "app/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
494
composer.lock
generated
Normal file
494
composer.lock
generated
Normal file
@@ -0,0 +1,494 @@
|
|||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "3bc14ffd1efccc6146ffadfb721bd3c1",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "graham-campbell/result-type",
|
||||||
|
"version": "v1.1.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/GrahamCampbell/Result-Type.git",
|
||||||
|
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b",
|
||||||
|
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.5"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GrahamCampbell\\ResultType\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "An Implementation Of The Result Type",
|
||||||
|
"keywords": [
|
||||||
|
"Graham Campbell",
|
||||||
|
"GrahamCampbell",
|
||||||
|
"Result Type",
|
||||||
|
"Result-Type",
|
||||||
|
"result"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
|
||||||
|
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-12-27T19:43:20+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpoption/phpoption",
|
||||||
|
"version": "1.9.5",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/schmittjoh/php-option.git",
|
||||||
|
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be",
|
||||||
|
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2.5 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.9-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpOption\\": "src/PhpOption/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"Apache-2.0"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Johannes M. Schmitt",
|
||||||
|
"email": "schmittjoh@gmail.com",
|
||||||
|
"homepage": "https://github.com/schmittjoh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Option Type for PHP",
|
||||||
|
"keywords": [
|
||||||
|
"language",
|
||||||
|
"option",
|
||||||
|
"php",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/schmittjoh/php-option/issues",
|
||||||
|
"source": "https://github.com/schmittjoh/php-option/tree/1.9.5"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-12-27T19:41:33+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-ctype",
|
||||||
|
"version": "v1.33.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
|
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||||
|
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-ctype": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-ctype": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Ctype\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Gert de Pagter",
|
||||||
|
"email": "BackEndTea@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for ctype functions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"ctype",
|
||||||
|
"polyfill",
|
||||||
|
"portable"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-09-09T11:45:10+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-mbstring",
|
||||||
|
"version": "v1.33.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
|
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-iconv": "*",
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-mbstring": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for the Mbstring extension",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"mbstring",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-12-23T08:48:59+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-php80",
|
||||||
|
"version": "v1.33.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
|
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||||
|
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Php80\\": ""
|
||||||
|
},
|
||||||
|
"classmap": [
|
||||||
|
"Resources/stubs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ion Bazan",
|
||||||
|
"email": "ion.bazan@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nicolas-grekas",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-01-02T08:10:11+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vlucas/phpdotenv",
|
||||||
|
"version": "v5.6.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||||
|
"reference": "955e7815d677a3eaa7075231212f2110983adecc"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc",
|
||||||
|
"reference": "955e7815d677a3eaa7075231212f2110983adecc",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-pcre": "*",
|
||||||
|
"graham-campbell/result-type": "^1.1.4",
|
||||||
|
"php": "^7.2.5 || ^8.0",
|
||||||
|
"phpoption/phpoption": "^1.9.5",
|
||||||
|
"symfony/polyfill-ctype": "^1.26",
|
||||||
|
"symfony/polyfill-mbstring": "^1.26",
|
||||||
|
"symfony/polyfill-php80": "^1.26"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-filter": "Required to use the boolean validator."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"bamarni-bin": {
|
||||||
|
"bin-links": true,
|
||||||
|
"forward-command": false
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "5.6-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Dotenv\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Graham Campbell",
|
||||||
|
"email": "hello@gjcampbell.co.uk",
|
||||||
|
"homepage": "https://github.com/GrahamCampbell"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vance Lucas",
|
||||||
|
"email": "vance@vancelucas.com",
|
||||||
|
"homepage": "https://github.com/vlucas"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||||
|
"keywords": [
|
||||||
|
"dotenv",
|
||||||
|
"env",
|
||||||
|
"environment"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/vlucas/phpdotenv/issues",
|
||||||
|
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/GrahamCampbell",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-12-27T19:49:13+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": {},
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"php": ">=8.0"
|
||||||
|
},
|
||||||
|
"platform-dev": {},
|
||||||
|
"plugin-api-version": "2.9.0"
|
||||||
|
}
|
||||||
14
example.htaccess
Normal file
14
example.htaccess
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
#RewriteBase /mse_api/
|
||||||
|
|
||||||
|
# 1. Prevent directory browsing
|
||||||
|
Options -Indexes
|
||||||
|
|
||||||
|
# 2. If the request is NOT a real directory...
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
# 3. ...and is NOT a real file...
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
# 4. ...send the request to index.php
|
||||||
|
RewriteRule ^ public/index.php [L]
|
||||||
22
getrequestheaders.php
Normal file
22
getrequestheaders.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if(!function_exists('getallheaders')){
|
||||||
|
http_response_code(403);
|
||||||
|
echo "Access Denied 900";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$headers = [];
|
||||||
|
foreach (getallheaders() as $name => $value) {
|
||||||
|
// echo "$name: $value <br>" . PHP_EOL;
|
||||||
|
$headers[$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
list($type, $bearer_data) = explode(" ", $headers['Authorization'], 2);
|
||||||
|
$retval = $auth->read_api_auth($bearer_data);
|
||||||
|
|
||||||
|
if ($retval == null) {
|
||||||
|
echo json_encode(['Access Denied']);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
23
info.md
Normal file
23
info.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Hi Kwesi,
|
||||||
|
|
||||||
|
This is what we agreed on the mse works:
|
||||||
|
|
||||||
|
You will work on disbursement confirmation endpoint
|
||||||
|
1. To sit on the mobile money server
|
||||||
|
2. Db is on .201 and called malawi_stock_exchange
|
||||||
|
3. The endpoint to accept our transid,their unique reference id, mobile number, amount, among others in the payload
|
||||||
|
4. The endpoint to respond back with status of the transaction
|
||||||
|
5. You should have another endpoint for transaction enquiry
|
||||||
|
6. This will just refer to the transaction enquiry service for disbursements
|
||||||
|
7. To accept the transID and unique reference in the payload
|
||||||
|
8. To return TS of TF to mean transaction successful or transaction failed respectively
|
||||||
|
9. All MSE projects are found on the mobile money server in this path:
|
||||||
|
10. Airtel Money: /var/www/payments.click-mobile.com/html/mse/airtelmoney
|
||||||
|
11. Mpamba: /var/www/payments.click-mobile.com/html/mse/mpamba
|
||||||
|
|
||||||
|
13. API - /var/www/payments.click-mobile.com/html/mse/api
|
||||||
|
14. http://216.55.185.131/
|
||||||
|
15. www.payments.click-mobile.com/api
|
||||||
|
|
||||||
|
|
||||||
|
Would you like to see how to define a global constant for this base URL so you can easily link to images and CSS from your templates?
|
||||||
50
mobile_money/airtelmoney/callback.php
Normal file
50
mobile_money/airtelmoney/callback.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
Developer: David Kumwenda
|
||||||
|
Contact: 0881161942 or 0996139030
|
||||||
|
App: Mpamba 4 MSE
|
||||||
|
Date: 30 August 2025
|
||||||
|
Duration: 1 day dev work*/
|
||||||
|
|
||||||
|
|
||||||
|
// Read raw POST data
|
||||||
|
$rawData = file_get_contents("php://input");
|
||||||
|
|
||||||
|
// Decode JSON
|
||||||
|
$data = json_decode($rawData, true);
|
||||||
|
|
||||||
|
// Basic logging (optional, useful for debugging)
|
||||||
|
file_put_contents("logs/callback_log_".date('Ymd').'.txt', date("Y-m-d H:i:s") . " - " . $rawData . PHP_EOL, FILE_APPEND); exit();
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (isset($data["receipt_number"], $data["result_code"], $data["transaction_id"])) {
|
||||||
|
|
||||||
|
$receiptNumber = $data["receipt_number"];
|
||||||
|
$resultCode = $data["result_code"];
|
||||||
|
$resultDescription = $data["result_description"] ?? "";
|
||||||
|
$resultTime = $data["result_time"] ?? date("Y-m-d H:i:s");
|
||||||
|
$transactionId = $data["transaction_id"];
|
||||||
|
$success = $data["success"] ?? false;
|
||||||
|
|
||||||
|
// Example: Save to database
|
||||||
|
// (Replace this with your actual DB insert/update code)
|
||||||
|
/*
|
||||||
|
$conn = mysqli_connect("localhost", "user", "password", "dbname");
|
||||||
|
$stmt = mysqli_prepare($conn, "INSERT INTO payments (transaction_id, receipt_number, result_code, result_description, result_time, success) VALUES (?, ?, ?, ?, ?, ?)");
|
||||||
|
mysqli_stmt_bind_param($stmt, "ssissi", $transactionId, $receiptNumber, $resultCode, $resultDescription, $resultTime, $success);
|
||||||
|
mysqli_stmt_execute($stmt);
|
||||||
|
mysqli_stmt_close($stmt);
|
||||||
|
mysqli_close($conn);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Respond with 200 OK to acknowledge receipt
|
||||||
|
http_response_code(200);
|
||||||
|
echo json_encode(["status" => "callback received"]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Missing required fields
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["error" => "Invalid callback payload"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
170
mobile_money/airtelmoney/check_balance.php
Normal file
170
mobile_money/airtelmoney/check_balance.php
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$clientID="94351d4d-4909-4056-ad9d-8052a332d6b9";
|
||||||
|
$clientSecret="bf665590-2519-49af-8d1f-7cd0dce1dc7a";
|
||||||
|
|
||||||
|
//CONTINENTAL CAPITAL
|
||||||
|
$clientID="9ff18a6d-331e-4ec5-9ecc-4e512e13747c";
|
||||||
|
$clientSecret="40f44254-10e7-4eb8-b161-38125117f4ba";
|
||||||
|
|
||||||
|
$authURL="https://openapiuat.airtel.africa/auth/oauth2/token";
|
||||||
|
$res=authenticate($authURL, $clientID, $clientSecret);
|
||||||
|
|
||||||
|
if($res['success']){
|
||||||
|
$bearerToken=$res['token'];
|
||||||
|
$country = "MW";
|
||||||
|
$currency = "MWK";
|
||||||
|
//enquire trans status
|
||||||
|
$res=getAirtelBalance($country, $currency, $bearerToken);
|
||||||
|
|
||||||
|
|
||||||
|
if ($res["status"] === "SUCCESS") {
|
||||||
|
echo "Balance: {$res['balance']} {$res['currency']}\n";
|
||||||
|
echo "Account Status: {$res['account_status']}";
|
||||||
|
} else {
|
||||||
|
echo "Error: " . $res["message"];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else{
|
||||||
|
echo(print_r($res,true));
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function getAirtelBalance($country, $currency, $token) {
|
||||||
|
$url = "https://openapiuat.airtel.africa/standard/v1/users/balance";
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$curl = curl_init();
|
||||||
|
|
||||||
|
curl_setopt_array($curl, [
|
||||||
|
CURLOPT_URL => $url,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_ENCODING => "",
|
||||||
|
CURLOPT_MAXREDIRS => 10,
|
||||||
|
CURLOPT_TIMEOUT => 30,
|
||||||
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||||
|
CURLOPT_CUSTOMREQUEST => "GET",
|
||||||
|
CURLOPT_HTTPHEADER => [
|
||||||
|
"Accept: application/json",
|
||||||
|
"X-Country: $country",
|
||||||
|
"X-Currency: $currency",
|
||||||
|
"Authorization: Bearer $token"
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = curl_exec($curl);
|
||||||
|
|
||||||
|
if (curl_errno($curl)) {
|
||||||
|
$error = curl_error($curl);
|
||||||
|
curl_close($curl);
|
||||||
|
return [
|
||||||
|
"status" => "ERROR",
|
||||||
|
"message" => "cURL Error: $error"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($curl);
|
||||||
|
|
||||||
|
// Decode response
|
||||||
|
$result = json_decode($response, true);
|
||||||
|
|
||||||
|
// Handle invalid JSON
|
||||||
|
if (!$result) {
|
||||||
|
return [
|
||||||
|
"status" => "ERROR",
|
||||||
|
"message" => "Invalid JSON response from Airtel API"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for API structure
|
||||||
|
if (!isset($result["status"])) {
|
||||||
|
return [
|
||||||
|
"status" => "ERROR",
|
||||||
|
"message" => "Unexpected API response format".print_r($result,true)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusCode = $result["status"]["code"] ?? null;
|
||||||
|
$message = $result["status"]["message"] ?? "Unknown error";
|
||||||
|
|
||||||
|
// ✅ SUCCESS case
|
||||||
|
if ($statusCode == "200" && isset($result["data"])) {
|
||||||
|
return [
|
||||||
|
"status" => "SUCCESS",
|
||||||
|
"balance" => $result["data"]["balance"] ?? "0",
|
||||||
|
"currency" => $result["data"]["currency"] ?? $currency,
|
||||||
|
"account_status" => $result["data"]["account_status"] ?? "Unknown"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ ERROR case (e.g. "User not found", "Invalid token", etc.)
|
||||||
|
return [
|
||||||
|
"status" => "ERROR",
|
||||||
|
"message" => $message,
|
||||||
|
"code" => $statusCode,
|
||||||
|
"responseCode" => $result["status"]["response_code"] ?? null,
|
||||||
|
"resultCode" => $result["status"]["result_code"] ?? null
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function authenticate($baseURL, $wallet, $password)
|
||||||
|
{
|
||||||
|
// JSON payload
|
||||||
|
$postData = json_encode([
|
||||||
|
'client_id' => $wallet,
|
||||||
|
'client_secret' => $password,
|
||||||
|
'grant_type' => "client_credentials"
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($baseURL);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
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)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
323
mobile_money/airtelmoney/disbursements.php
Normal file
323
mobile_money/airtelmoney/disbursements.php
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
<?php
|
||||||
|
$incoming=file_get_contents("php://input");
|
||||||
|
$data=json_decode($incoming,true);
|
||||||
|
|
||||||
|
|
||||||
|
$subscriber_country = $data['country'];
|
||||||
|
$subscriber_currency = $data['currency'];
|
||||||
|
$subscriber_msisdn = $data['msisdn'];
|
||||||
|
|
||||||
|
$transaction_amount = $data['amount'];
|
||||||
|
$transaction_country = $data['country'];
|
||||||
|
$transaction_currency = $data['currency'];
|
||||||
|
$transaction_id = $data['transactionID'];
|
||||||
|
|
||||||
|
|
||||||
|
$authURL="https://openapiuat.airtel.mw/auth/oauth2/token";
|
||||||
|
/*CEDAR CAPITAL
|
||||||
|
$clientID="94351d4d-4909-4056-ad9d-8052a332d6b9";
|
||||||
|
$clientSecret="bf665590-2519-49af-8d1f-7cd0dce1dc7a";*/
|
||||||
|
|
||||||
|
|
||||||
|
//CONTINENTAL CAPITAL DISBURSEMENTS
|
||||||
|
$clientID="7f96ba01-2a74-4342-a50d-f0a36874f985";
|
||||||
|
$clientSecret="d31861d6-125e-4c0f-a0b3-2bca7af3cad0";
|
||||||
|
|
||||||
|
//CEDAR DISBURSEMENTS
|
||||||
|
//$clientID="0ffd5cef-40f6-4673-8620-9ace5c798364";
|
||||||
|
//$clientSecret="15fdc659-1431-4536-8c95-4307857464ce";
|
||||||
|
|
||||||
|
//STOCK BROKERS DISBURSEMENTS [not approved]
|
||||||
|
//$clientID="9ff18a6d-331e-4ec5-9ecc-4e512e13747c";
|
||||||
|
//$clientSecret="40f44254-10e7-4eb8-b161-38125117f4ba";
|
||||||
|
|
||||||
|
|
||||||
|
$res=authenticate($authURL, $clientID, $clientSecret);
|
||||||
|
|
||||||
|
if($res['success']){
|
||||||
|
$bearerToken=$res['token'];
|
||||||
|
|
||||||
|
echo $bearerToken;
|
||||||
|
|
||||||
|
//send a payment request
|
||||||
|
$payment = airtelDisbursement(
|
||||||
|
$bearerToken,
|
||||||
|
$subscriber_msisdn,
|
||||||
|
$transaction_amount,
|
||||||
|
'MSEMW',
|
||||||
|
'IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=',
|
||||||
|
uniqid('mse-')
|
||||||
|
);
|
||||||
|
|
||||||
|
echo (json_encode($payment));
|
||||||
|
//$data = json_decode($res, true);
|
||||||
|
|
||||||
|
/* Check if the response has a status and success flag
|
||||||
|
if (isset($data['status']['success']) && $data['status']['success'] === true) {
|
||||||
|
// Success case
|
||||||
|
$transactionId = $data['data']['transaction']['id'];
|
||||||
|
$transactionStatus = $data['data']['transaction']['status'];
|
||||||
|
$message = $data['status']['message'];
|
||||||
|
|
||||||
|
echo "✅ Transaction Successful!\n";
|
||||||
|
echo "Transaction ID: $transactionId\n";
|
||||||
|
echo "Status: $transactionStatus\n";
|
||||||
|
echo "Message: $message\n";
|
||||||
|
} else {
|
||||||
|
// Failure case
|
||||||
|
$errorCode = $data['status']['result_code'] ?? 'N/A';
|
||||||
|
$errorMessage = $data['status']['message'] ?? 'Unknown error';
|
||||||
|
|
||||||
|
echo "❌ Transaction Failed!\n";
|
||||||
|
echo "Error Code: $errorCode\n";
|
||||||
|
echo "Message: $errorMessage\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
}else{
|
||||||
|
echo(json_encode($res));
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function airtelDisbursement(string $accessToken,string $msisdn,float $amount,string $reference,string $pin,string $transactionId,
|
||||||
|
string $country = 'MW',string $currency = 'MWK',int $timeout = 30) {
|
||||||
|
|
||||||
|
$url = 'https://openapiuat.airtel.mw/standard/v1/disbursements/';
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
"payee" => [
|
||||||
|
"msisdn" => $msisdn
|
||||||
|
],
|
||||||
|
"reference" => $reference,
|
||||||
|
"pin" => $pin,
|
||||||
|
"transaction" => [
|
||||||
|
"amount" => $amount,
|
||||||
|
"id" => $transactionId
|
||||||
|
]
|
||||||
|
];
|
||||||
|
file_put_contents('logs/requests.txt',json_encode($payload).PHP_EOL,FILE_APPEND);
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'Accept: */*',
|
||||||
|
'X-Country: ' . $country,
|
||||||
|
'X-Currency: ' . $currency,
|
||||||
|
'Authorization: Bearer ' . $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
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => ($code === 200),
|
||||||
|
'http_code' => $code,
|
||||||
|
'response' => json_decode($response, true),
|
||||||
|
'raw' => $response
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sendUSSDPush($token, $data) {
|
||||||
|
// Endpoint
|
||||||
|
$url = "https://openapiuat.airtel.africa/merchant/v1/payments/";
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer " . $token,
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"X-Country: MW",
|
||||||
|
"X-Currency: MWK"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||||
|
|
||||||
|
// Execute request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
echo "cURL Error: " . curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode and return response
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePassword($baseURL, $token,$newPassword, $newPasswordConfirmation) {
|
||||||
|
// Endpoint URL
|
||||||
|
$url = rtrim($baseURL, "/") . "/password";
|
||||||
|
|
||||||
|
// Prepare data
|
||||||
|
$data = [
|
||||||
|
"new_password" => $newPassword,
|
||||||
|
"new_password_confirmation" => $newPasswordConfirmation
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); // PATCH request
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer " . $token,
|
||||||
|
"Content-Type: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||||
|
|
||||||
|
// Execute and capture response
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
echo "cURL Error: " . curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode JSON response
|
||||||
|
return json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate_msisdn($baseURL, $msisdn, $bearerToken)
|
||||||
|
{
|
||||||
|
// Ensure proper endpoint format
|
||||||
|
$url = rtrim($baseURL, '/') . '/payments/validate/' . urlencode($msisdn);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Authorization: Bearer ' . $bearerToken,
|
||||||
|
'Accept: application/json'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Handle cURL error
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get HTTP status code
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode JSON response
|
||||||
|
$result = json_decode($response, true);
|
||||||
|
|
||||||
|
if ($httpCode === 200 && isset($result['data']['full_name'])) {
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'full_name' => $result['data']['full_name']
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => $result['message'] ?? 'Unknown error',
|
||||||
|
'details' => $result['errors'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function authenticate($baseURL, $wallet, $password)
|
||||||
|
{
|
||||||
|
// JSON payload
|
||||||
|
$postData = json_encode([
|
||||||
|
'client_id' => $wallet,
|
||||||
|
'client_secret' => $password,
|
||||||
|
'grant_type' => "client_credentials"
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($baseURL);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
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)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
8
mobile_money/airtelmoney/logs/callback_log_20251024.txt
Normal file
8
mobile_money/airtelmoney/logs/callback_log_20251024.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
2025-10-24 04:47:56 - {
|
||||||
|
"transaction": {
|
||||||
|
"id": "BBZMiscxy",
|
||||||
|
"message": "Paid UGX 5,000 to TECHNOLOGIES LIMITED Charge UGX 140, Trans ID MP210603.1234.L06941.",
|
||||||
|
"status_code": "TS",
|
||||||
|
"airtel_money_id": "MP210603.1234.L06941"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
mobile_money/airtelmoney/logs/callback_log_20251107.txt
Normal file
5
mobile_money/airtelmoney/logs/callback_log_20251107.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
2025-11-07 13:14:45 -
|
||||||
|
2025-11-07 13:15:13 -
|
||||||
|
2025-11-07 13:15:31 -
|
||||||
|
2025-11-07 18:36:52 -
|
||||||
|
2025-11-07 19:00:47 -
|
||||||
1
mobile_money/airtelmoney/logs/callback_log_20260212.txt
Normal file
1
mobile_money/airtelmoney/logs/callback_log_20260212.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2026-02-12 08:45:17 - {"transaction":{"status_code":"TF","code":"DP00800001005","airtel_money_id":"null","id":"1234567919","message":"Transaction id is invalid"}}
|
||||||
44
mobile_money/airtelmoney/logs/requests.txt
Normal file
44
mobile_money/airtelmoney/logs/requests.txt
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSE","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a54c4b7e054"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSE","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a54d2c75c62"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSE","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a54d3ceab6e"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSE","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a58de1834d8"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSE","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a58ed675c14"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSE","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a58fc2bb56d"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a5906ab20c0"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a590f316b25"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a5942d5434d"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a59b2c6a361"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a5a35dbf63e"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a9293a81d78"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a9310f9dd96"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse_69a931d2eb29e"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse-69aa7f7549067"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse-69aa7ff5ea099"}}
|
||||||
|
{"payee":{"msisdn":"996139030"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000,"id":"mse-69aa80405eef1"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":100,"id":"mse-69aa88596095c"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":100,"id":"mse-69aa8960bca7f"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":100,"id":"mse-69aa89ab291dc"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":10,"id":"mse-69aa89ca9dd83"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":50,"id":"mse-69aa89fc03810"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":49,"id":"mse-69aa8a286793a"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":200000,"id":"mse-69aa8a5b59210"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":200.5,"id":"mse-69aa8ace57745"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":0,"id":"mse-69aa8b45944b1"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":0,"id":"mse-69aa8bd951d17"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":800000,"id":"mse-69aa8c56e961a"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1800000,"id":"mse-69aa8d48d4725"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000000,"id":"mse-69aa8da5e5a46"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1000001,"id":"mse-69aa8db2be4d7"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":100,"id":"mse-69aa8de74fa4d"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":100,"id":"mse-69aa8df1239ab"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":100,"id":"mse-69aa8effbeb72"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":50.5,"id":"mse-69aa8f3cc16fd"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":550.5,"id":"mse-69aa8fa266f85"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":50,"id":"mse-69aa90141ef14"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":50,"id":"mse-69aa90355ff2f"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":200,"id":"mse-69aa906ab50d9"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":200000,"id":"mse-69aa90d3ea868"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":1200000,"id":"mse-69aa910b28409"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":10,"id":"mse-69aa9386358f9"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":50,"id":"mse-69aa94788ea87"}}
|
||||||
|
{"payee":{"msisdn":"999959024"},"reference":"MSEMW","pin":"IGbCqXwRoiqsHTIIjxfo6vWyzPMKg6iF3+pNQK6gTXbOyJgOd1bbPuIstTcMwSAiRXOgQrkRC0+sQU5wHF33aha+AL0TevBntLzVyGl8002ZXy6Ux4Pu+zymRdlw7J6H\/PXRC2kXhbR2GIHLHlqHC49gu65OzpJ8fvpnscg1yjE=","transaction":{"amount":50,"id":"mse-69c636d71e959"}}
|
||||||
250
mobile_money/airtelmoney/testing.php
Normal file
250
mobile_money/airtelmoney/testing.php
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
<?php
|
||||||
|
$incoming=file_get_contents("php://input");
|
||||||
|
$data=json_decode($incoming,true);
|
||||||
|
|
||||||
|
|
||||||
|
$subscriber_country = $data['subscriber']['country'];
|
||||||
|
$subscriber_currency = $data['subscriber']['currency'];
|
||||||
|
$subscriber_msisdn = $data['subscriber']['msisdn'];
|
||||||
|
|
||||||
|
$transaction_amount = $data['transaction']['amount'];
|
||||||
|
$transaction_country = $data['transaction']['country'];
|
||||||
|
$transaction_currency = $data['transaction']['currency'];
|
||||||
|
$transaction_id = $data['transaction']['id'];
|
||||||
|
|
||||||
|
|
||||||
|
$authURL="https://openapi.airtel.mw/auth/oauth2/token";
|
||||||
|
/*CEDAR CAPITAL
|
||||||
|
$clientID="94351d4d-4909-4056-ad9d-8052a332d6b9";
|
||||||
|
$clientSecret="bf665590-2519-49af-8d1f-7cd0dce1dc7a";*/
|
||||||
|
|
||||||
|
|
||||||
|
//CONTINENTAL CAPITAL UAT
|
||||||
|
$clientID="9ff18a6d-331e-4ec5-9ecc-4e512e13747c";
|
||||||
|
$clientSecret="40f44254-10e7-4eb8-b161-38125117f4ba";
|
||||||
|
|
||||||
|
//CONTINENTAL CAPITAL PROD
|
||||||
|
$clientID="37063c29-d090-4133-8a6c-6f3295d7c2a9";
|
||||||
|
$clientSecret="6502ff2d-be8d-485f-8fa4-d03e2d8ed11d";
|
||||||
|
|
||||||
|
|
||||||
|
$res=authenticate($authURL, $clientID, $clientSecret);
|
||||||
|
|
||||||
|
if($res['success']){
|
||||||
|
$bearerToken=$res['token'];
|
||||||
|
|
||||||
|
//send a ussd push
|
||||||
|
$res=sendUSSDPush($bearerToken, $data);
|
||||||
|
$data = json_decode($res, true);
|
||||||
|
|
||||||
|
// Check if the response has a status and success flag
|
||||||
|
if (isset($data['status']['success']) && $data['status']['success'] === true) {
|
||||||
|
// Success case
|
||||||
|
$transactionId = $data['data']['transaction']['id'];
|
||||||
|
$transactionStatus = $data['data']['transaction']['status'];
|
||||||
|
$message = $data['status']['message'];
|
||||||
|
|
||||||
|
echo "✅ Transaction Successful!\n";
|
||||||
|
echo "Transaction ID: $transactionId\n";
|
||||||
|
echo "Status: $transactionStatus\n";
|
||||||
|
echo "Message: $message\n";
|
||||||
|
} else {
|
||||||
|
// Failure case
|
||||||
|
$errorCode = $data['status']['result_code'] ?? 'N/A';
|
||||||
|
$errorMessage = $data['status']['message'] ?? 'Unknown error';
|
||||||
|
|
||||||
|
echo "❌ Transaction Failed!\n";
|
||||||
|
echo "Error Code: $errorCode\n";
|
||||||
|
echo "Message: $errorMessage\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else{
|
||||||
|
echo(print_r($res,true));
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function sendUSSDPush($token, $data) {
|
||||||
|
// Endpoint
|
||||||
|
$url = "https://openapi.airtel.mw/merchant/v1/payments/";
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer " . $token,
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"X-Country: MW",
|
||||||
|
"X-Currency: MWK"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||||
|
|
||||||
|
// Execute request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
echo "cURL Error: " . curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode and return response
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePassword($baseURL, $token,$newPassword, $newPasswordConfirmation) {
|
||||||
|
// Endpoint URL
|
||||||
|
$url = rtrim($baseURL, "/") . "/password";
|
||||||
|
|
||||||
|
// Prepare data
|
||||||
|
$data = [
|
||||||
|
"new_password" => $newPassword,
|
||||||
|
"new_password_confirmation" => $newPasswordConfirmation
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); // PATCH request
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer " . $token,
|
||||||
|
"Content-Type: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||||
|
|
||||||
|
// Execute and capture response
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
echo "cURL Error: " . curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode JSON response
|
||||||
|
return json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate_msisdn($baseURL, $msisdn, $bearerToken)
|
||||||
|
{
|
||||||
|
// Ensure proper endpoint format
|
||||||
|
$url = rtrim($baseURL, '/') . '/payments/validate/' . urlencode($msisdn);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Authorization: Bearer ' . $bearerToken,
|
||||||
|
'Accept: application/json'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Handle cURL error
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get HTTP status code
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode JSON response
|
||||||
|
$result = json_decode($response, true);
|
||||||
|
|
||||||
|
if ($httpCode === 200 && isset($result['data']['full_name'])) {
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'full_name' => $result['data']['full_name']
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => $result['message'] ?? 'Unknown error',
|
||||||
|
'details' => $result['errors'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function authenticate($baseURL, $wallet, $password)
|
||||||
|
{
|
||||||
|
// JSON payload
|
||||||
|
$postData = json_encode([
|
||||||
|
'client_id' => $wallet,
|
||||||
|
'client_secret' => $password,
|
||||||
|
'grant_type' => "client_credentials"
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($baseURL);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
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)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
142
mobile_money/airtelmoney/trans_enquiry.php
Normal file
142
mobile_money/airtelmoney/trans_enquiry.php
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
//$requestUri = $_SERVER['REQUEST_URI'];
|
||||||
|
//$parts = explode('/', trim($requestUri, '/'));
|
||||||
|
$id = $_GET['id'];//end($parts);
|
||||||
|
//echo $id;
|
||||||
|
|
||||||
|
|
||||||
|
$enqURL="https://openapiuat.airtel.africa/standard/v1/payments/".$id;
|
||||||
|
|
||||||
|
$authURL="https://openapiuat.airtel.africa/auth/oauth2/token";
|
||||||
|
|
||||||
|
$clientID="94351d4d-4909-4056-ad9d-8052a332d6b9";
|
||||||
|
$clientSecret="bf665590-2519-49af-8d1f-7cd0dce1dc7a";
|
||||||
|
|
||||||
|
//CONTINENTAL CAPITAL
|
||||||
|
$clientID="9ff18a6d-331e-4ec5-9ecc-4e512e13747c";
|
||||||
|
$clientSecret="40f44254-10e7-4eb8-b161-38125117f4ba";
|
||||||
|
|
||||||
|
$res=authenticate($authURL, $clientID, $clientSecret);
|
||||||
|
|
||||||
|
if($res['success']){
|
||||||
|
$bearerToken=$res['token'];
|
||||||
|
|
||||||
|
//enquire trans status
|
||||||
|
$res=trans_enquiry($enqURL,$bearerToken);
|
||||||
|
|
||||||
|
echo $res;
|
||||||
|
exit();
|
||||||
|
$data = json_decode($res, true);
|
||||||
|
|
||||||
|
// Check if the response has a status and success flag
|
||||||
|
if (isset($data['status']['success']) && $data['status']['success'] === true) {
|
||||||
|
// Success case
|
||||||
|
$transactionId = $data['data']['transaction']['id'];
|
||||||
|
$transactionStatus = $data['data']['transaction']['status'];
|
||||||
|
$message = $data['status']['message'];
|
||||||
|
|
||||||
|
echo "✅ Transaction Successful!\n";
|
||||||
|
echo "Transaction ID: $transactionId\n";
|
||||||
|
echo "Status: $transactionStatus\n";
|
||||||
|
echo "Message: $message\n";
|
||||||
|
} else {
|
||||||
|
// Failure case
|
||||||
|
$errorCode = $data['status']['result_code'] ?? 'N/A';
|
||||||
|
$errorMessage = $data['status']['message'] ?? 'Unknown error';
|
||||||
|
|
||||||
|
echo "❌ Transaction Failed!\n";
|
||||||
|
echo "Error Code: $errorCode\n";
|
||||||
|
echo "Message: $errorMessage\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else{
|
||||||
|
echo(print_r($res,true));
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function trans_enquiry($enqURL,$token){
|
||||||
|
$curl = curl_init();
|
||||||
|
|
||||||
|
curl_setopt_array($curl, array(
|
||||||
|
CURLOPT_URL => $enqURL,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_ENCODING => '',
|
||||||
|
CURLOPT_MAXREDIRS => 10,
|
||||||
|
CURLOPT_TIMEOUT => 0,
|
||||||
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||||
|
CURLOPT_CUSTOMREQUEST => 'GET',
|
||||||
|
CURLOPT_HTTPHEADER => array(
|
||||||
|
'Authorization:'.$token,
|
||||||
|
'Accept: */* ',
|
||||||
|
'X-Country: MW',
|
||||||
|
'X-Currency: MWK'
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
$response = curl_exec($curl);
|
||||||
|
|
||||||
|
curl_close($curl);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function authenticate($baseURL, $wallet, $password)
|
||||||
|
{
|
||||||
|
// JSON payload
|
||||||
|
$postData = json_encode([
|
||||||
|
'client_id' => $wallet,
|
||||||
|
'client_secret' => $password,
|
||||||
|
'grant_type' => "client_credentials"
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($baseURL);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
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)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
50
mobile_money/mpamba/callback.php
Normal file
50
mobile_money/mpamba/callback.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
Developer: David Kumwenda
|
||||||
|
Contact: 0881161942 or 0996139030
|
||||||
|
App: Mpamba 4 MSE
|
||||||
|
Date: 30 August 2025
|
||||||
|
Duration: 1 day dev work*/
|
||||||
|
|
||||||
|
|
||||||
|
// Read raw POST data
|
||||||
|
$rawData = file_get_contents("php://input");
|
||||||
|
|
||||||
|
// Decode JSON
|
||||||
|
$data = json_decode($rawData, true);
|
||||||
|
|
||||||
|
// Basic logging (optional, useful for debugging)
|
||||||
|
file_put_contents("logs/callback_log_".date('Ymd').'.txt', date("Y-m-d H:i:s") . " - " . $rawData . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (isset($data["receipt_number"], $data["result_code"], $data["transaction_id"])) {
|
||||||
|
|
||||||
|
$receiptNumber = $data["receipt_number"];
|
||||||
|
$resultCode = $data["result_code"];
|
||||||
|
$resultDescription = $data["result_description"] ?? "";
|
||||||
|
$resultTime = $data["result_time"] ?? date("Y-m-d H:i:s");
|
||||||
|
$transactionId = $data["transaction_id"];
|
||||||
|
$success = $data["success"] ?? false;
|
||||||
|
|
||||||
|
// Example: Save to database
|
||||||
|
// (Replace this with your actual DB insert/update code)
|
||||||
|
/*
|
||||||
|
$conn = mysqli_connect("localhost", "user", "password", "dbname");
|
||||||
|
$stmt = mysqli_prepare($conn, "INSERT INTO payments (transaction_id, receipt_number, result_code, result_description, result_time, success) VALUES (?, ?, ?, ?, ?, ?)");
|
||||||
|
mysqli_stmt_bind_param($stmt, "ssissi", $transactionId, $receiptNumber, $resultCode, $resultDescription, $resultTime, $success);
|
||||||
|
mysqli_stmt_execute($stmt);
|
||||||
|
mysqli_stmt_close($stmt);
|
||||||
|
mysqli_close($conn);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Respond with 200 OK to acknowledge receipt
|
||||||
|
http_response_code(200);
|
||||||
|
echo json_encode(["status" => "callback received"]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Missing required fields
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["error" => "Invalid callback payload"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
5
mobile_money/mpamba/index.php
Normal file
5
mobile_money/mpamba/index.php
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
$data=file_get_contents('php://input');
|
||||||
|
echo 'This is the correct path.';
|
||||||
|
|
||||||
|
?>
|
||||||
1
mobile_money/mpamba/logs/callback_20250829.txt
Normal file
1
mobile_money/mpamba/logs/callback_20250829.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"Hi":"Hello"}
|
||||||
2
mobile_money/mpamba/logs/callback_log_20250901.txt
Normal file
2
mobile_money/mpamba/logs/callback_log_20250901.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
2025-09-01 09:02:45 - {"receipt_number":"10887331688742698041","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-01 11:02:44","transaction_id":"cedar6","success":false}
|
||||||
|
2025-09-01 09:03:42 - {"receipt_number":"10003173421101776517","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-01 11:03:42","transaction_id":"cedar7","success":false}
|
||||||
1
mobile_money/mpamba/logs/callback_log_20250908.txt
Normal file
1
mobile_money/mpamba/logs/callback_log_20250908.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2025-09-08 08:05:34 - {"receipt_number":"10004158814706547506","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-08 10:05:33","transaction_id":"cedar10","success":false}
|
||||||
15
mobile_money/mpamba/logs/callback_log_20250917.txt
Normal file
15
mobile_money/mpamba/logs/callback_log_20250917.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
2025-09-17 09:10:09 - {"receipt_number":"CIH0000000","result_description":": Invalid Account Number","result_code":2002,"result_time":"2025-09-17 11:10:08","transaction_id":"cedar12","success":false}
|
||||||
|
2025-09-17 09:10:55 - {"receipt_number":"10216260777279973276","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-17 11:10:55","transaction_id":"cedar11","success":false}
|
||||||
|
2025-09-17 09:21:36 - {"receipt_number":"CIH22CNO3O4","result_description":": Process service request successfully.","result_code":0,"result_time":"2025-09-17 11:21:35","transaction_id":"cedar14","success":true}
|
||||||
|
2025-09-17 09:26:28 - {"receipt_number":"CIH42CNO3O6","result_description":": Balance insufficient.","result_code":2006,"result_time":"2025-09-17 11:26:27","transaction_id":"cedar15","success":false}
|
||||||
|
2025-09-17 09:31:58 - {"receipt_number":"10395173983942501327","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-17 11:31:57","transaction_id":"cedar16","success":false}
|
||||||
|
2025-09-17 09:35:40 - {"receipt_number":"CIH32CNO3OF","result_description":": Balance insufficient.","result_code":2006,"result_time":"2025-09-17 11:35:39","transaction_id":"cedar19","success":false}
|
||||||
|
2025-09-17 09:35:52 - {"receipt_number":"10839014874366890664","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-17 11:35:51","transaction_id":"cedar17","success":false}
|
||||||
|
2025-09-17 09:36:25 - {"receipt_number":"10016670262346814213","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-17 11:36:24","transaction_id":"cedar18","success":false}
|
||||||
|
2025-09-17 09:41:23 - {"receipt_number":"CIH92CNO3OL","result_description":": Balance insufficient.","result_code":2006,"result_time":"2025-09-17 11:41:22","transaction_id":"cedar22","success":false}
|
||||||
|
2025-09-17 09:41:41 - {"receipt_number":"10694042274274847334","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-17 11:41:40","transaction_id":"cedar20","success":false}
|
||||||
|
2025-09-17 09:42:11 - {"receipt_number":"10310137165383093703","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-17 11:42:10","transaction_id":"cedar21","success":false}
|
||||||
|
2025-09-17 09:46:38 - {"receipt_number":"CIH32CNO3OP","result_description":": Process service request successfully.","result_code":0,"result_time":"2025-09-17 11:46:37","transaction_id":"cedar23","success":true}
|
||||||
|
2025-09-17 09:49:16 - {"receipt_number":"CIH0000000","result_description":": Amount invalid.","result_code":2004,"result_time":"2025-09-17 11:49:15","transaction_id":"cedar24","success":false}
|
||||||
|
2025-09-17 09:52:11 - {"receipt_number":"10716865356500456126","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-17 11:52:10","transaction_id":"cedar25","success":false}
|
||||||
|
2025-09-17 09:54:17 - {"receipt_number":"10988964508151890908","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-09-17 11:54:17","transaction_id":"cedar26","success":false}
|
||||||
2
mobile_money/mpamba/logs/callback_log_20251119.txt
Normal file
2
mobile_money/mpamba/logs/callback_log_20251119.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
2025-11-19 11:35:02 - {"receipt_number":"10478257471611622849","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-11-19 13:34:56","transaction_id":"12345666","success":false}
|
||||||
|
2025-11-19 11:46:47 - {"receipt_number":"10847714824278927203","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-11-19 13:46:41","transaction_id":"12345667","success":false}
|
||||||
2
mobile_money/mpamba/logs/callback_log_20251126.txt
Normal file
2
mobile_money/mpamba/logs/callback_log_20251126.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
2025-11-26 11:22:08 - {"receipt_number":"10342708118343667220","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-11-26 13:22:02","transaction_id":"kiddo2","success":false}
|
||||||
|
2025-11-26 11:25:22 - {"receipt_number":"10498981254538029140","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-11-26 13:25:15","transaction_id":"kiddo3","success":false}
|
||||||
14
mobile_money/mpamba/logs/callback_log_20251218.txt
Normal file
14
mobile_money/mpamba/logs/callback_log_20251218.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
2025-12-18 07:59:16 - {"receipt_number":"10225876409109341761","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-12-18 09:59:15","transaction_id":"12345771","success":false}
|
||||||
|
2025-12-18 08:08:28 - {"receipt_number":"CLI0000000","result_description":": Invalid Account Number","result_code":2002,"result_time":"2025-12-18 10:08:28","transaction_id":"12345772","success":false}
|
||||||
|
2025-12-18 08:16:03 - {"receipt_number":"CLI72HP9KFT","result_description":": Process service request successfully.","result_code":0,"result_time":"2025-12-18 10:16:02","transaction_id":"12345773","success":true}
|
||||||
|
2025-12-18 08:41:39 - {"receipt_number":"CLI42HP9KG0","result_description":": Process service request successfully.","result_code":0,"result_time":"2025-12-18 10:41:38","transaction_id":"12345775","success":true}
|
||||||
|
2025-12-18 08:42:12 - {"receipt_number":"10615975978933751017","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-12-18 10:42:11","transaction_id":"12345774","success":false}
|
||||||
|
2025-12-18 09:04:18 - {"receipt_number":"CLI02HP9KG6","result_description":": Process service request successfully.","result_code":0,"result_time":"2025-12-18 11:04:17","transaction_id":"12345778","success":true}
|
||||||
|
2025-12-18 09:05:26 - {"receipt_number":"10025084431939463372","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-12-18 11:05:25","transaction_id":"12345777","success":false}
|
||||||
|
2025-12-18 09:08:56 - {"receipt_number":"CLI22HP9KG8","result_description":": Balance insufficient.","result_code":2006,"result_time":"2025-12-18 11:08:56","transaction_id":"12345779","success":false}
|
||||||
|
2025-12-18 09:11:53 - {"receipt_number":"CLI52HP9KGB","result_description":": Process service request successfully.","result_code":0,"result_time":"2025-12-18 11:11:52","transaction_id":"12345780","success":true}
|
||||||
|
2025-12-18 09:13:41 - {"receipt_number":"CLI72HP9KGD","result_description":": Rule limited.","result_code":2005,"result_time":"2025-12-18 11:13:40","transaction_id":"12345781","success":false}
|
||||||
|
2025-12-18 09:15:34 - {"receipt_number":"CLI92HP9KGF","result_description":": Balance insufficient.","result_code":2006,"result_time":"2025-12-18 11:15:34","transaction_id":"12345782","success":false}
|
||||||
|
2025-12-18 09:16:48 - {"receipt_number":"CLI22HP9KGI","result_description":": Balance insufficient.","result_code":2006,"result_time":"2025-12-18 11:16:48","transaction_id":"12345783","success":false}
|
||||||
|
2025-12-18 09:27:41 - {"receipt_number":"10280516611960183870","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-12-18 11:27:40","transaction_id":"12345785","success":false}
|
||||||
|
2025-12-18 09:33:07 - {"receipt_number":"10729630476149503786","result_description":": Process service request successfully.","result_code":402,"result_time":"2025-12-18 11:33:06","transaction_id":"12345786","success":false}
|
||||||
1
mobile_money/mpamba/logs/callback_log_20260129.txt
Normal file
1
mobile_money/mpamba/logs/callback_log_20260129.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2026-01-29 23:40:42 - {"receipt_number":"10230529140546671414","result_description":": Process service request successfully.","result_code":402,"result_time":"2026-01-30 01:40:42","transaction_id":"12345789","success":false}
|
||||||
2
mobile_money/mpamba/logs/callback_log_20260130.txt
Normal file
2
mobile_money/mpamba/logs/callback_log_20260130.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
2026-01-30 00:41:32 - {"receipt_number":"10369576091684466032","result_description":": Process service request successfully.","result_code":402,"result_time":"2026-01-30 02:41:31","transaction_id":"10388103","success":false}
|
||||||
|
2026-01-30 00:59:38 - {"receipt_number":"10345565245761348404","result_description":": Process service request successfully.","result_code":402,"result_time":"2026-01-30 02:59:37","transaction_id":"10389339","success":false}
|
||||||
1
mobile_money/mpamba/logs/callback_log_20260203.txt
Normal file
1
mobile_money/mpamba/logs/callback_log_20260203.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2026-02-03 00:00:53 - {"receipt_number":"10931861926510111848","result_description":": Process service request successfully.","result_code":402,"result_time":"2026-02-03 02:00:52","transaction_id":"3805907571","success":false}
|
||||||
311
mobile_money/mpamba/payments.php
Normal file
311
mobile_money/mpamba/payments.php
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
<?php
|
||||||
|
$incoming=file_get_contents("php://input");
|
||||||
|
$details=json_decode($incoming,true);
|
||||||
|
|
||||||
|
$transactionId=$details['transactionId'];
|
||||||
|
$amount=$details['amount'];
|
||||||
|
$msisdn=$details['msisdn'];
|
||||||
|
$description=$details['description'];
|
||||||
|
|
||||||
|
|
||||||
|
$baseURL="https://devpayouts.tnmmpamba.co.mw/api";
|
||||||
|
$wallet="505073";
|
||||||
|
$password="N8O7L0vpl5mflfwHzf4DSle9bToV*";//CEDAR PASSWORD & All other wallets
|
||||||
|
|
||||||
|
$res=authenticate($baseURL, $wallet, $password);
|
||||||
|
|
||||||
|
//echo print_r($res,true);
|
||||||
|
$bearerToken=$res['token'];//"102164|newoEd6QOFaTptUiGAp232jULtUmgMtaX1x2CRww4Ka2270dc49";
|
||||||
|
|
||||||
|
//echo $bearerToken;
|
||||||
|
|
||||||
|
//$res=validate_msisdn($baseURL, '265881161942', $bearerToken);
|
||||||
|
//$res=changePassword($baseURL, $bearerToken, "N8O7L0vpl5mflfwHzf4DSle9bToV*", "N8O7L0vpl5mflfwHzf4DSle9bToV*");
|
||||||
|
//$res=sendUSSDPush($baseURL, $bearerToken, $invoiceNumber, $amount, $msisdn, $description);
|
||||||
|
$result=makePayment($baseURL,$bearerToken,$msisdn, $amount, $transactionId, $description);
|
||||||
|
echo (print_r($result,true));
|
||||||
|
|
||||||
|
if (!isset($result['http_code'])) {
|
||||||
|
// Network or cURL failure
|
||||||
|
echo "SYSTEM ERROR: " . $result['message'];
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($result['http_code']) {
|
||||||
|
|
||||||
|
case 200:
|
||||||
|
// Completed successfully
|
||||||
|
$transID = $result['response']['data']['transaction_id'] ?? '';
|
||||||
|
$receipt = $result['response']['data']['receipt_number'] ?? '';
|
||||||
|
echo "SUCCESS: Payment completed. Transaction ID: $transID Receipt: " . $receipt;
|
||||||
|
|
||||||
|
//mark transaction as SUCCESSFUL in db later
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 202:
|
||||||
|
// Accepted but pending
|
||||||
|
echo "PENDING: Transaction accepted and processing.";
|
||||||
|
|
||||||
|
//mark transaction as PENDING / SUSPENSE in the db
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 400:
|
||||||
|
// Invalid request
|
||||||
|
echo "FAILED: Bad request. Check parameters.";
|
||||||
|
|
||||||
|
//mark transaction as FAILED in the db
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 503:
|
||||||
|
// Service unavailable
|
||||||
|
echo "FAILED: Service unavailable. Try again later.";
|
||||||
|
|
||||||
|
//mark transaction as FAILED or RETRY later after setting a retry cron
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
echo "UNKNOWN RESPONSE: " . $result['http_code'];
|
||||||
|
//unkown response, do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function makePayment($baseURL,$bearerToken,$msisdn, $amount, $transactionId, $description)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$url = $baseURL . "/payments";
|
||||||
|
|
||||||
|
// Request payload
|
||||||
|
$payload = array(
|
||||||
|
"msisdn" => $msisdn,
|
||||||
|
"amount" => (int)$amount,
|
||||||
|
"transaction_id" => $transactionId,
|
||||||
|
"narration" => $description
|
||||||
|
);
|
||||||
|
|
||||||
|
$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 " . $bearerToken
|
||||||
|
));
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
$error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
"status" => "ERROR",
|
||||||
|
"message" => "cURL error: " . $error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$responseData = json_decode($response, true);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
"http_code" => $httpCode,
|
||||||
|
"response" => $responseData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sendUSSDPush($baseURL, $token, $invoiceNumber, $amount, $msisdn, $description) {
|
||||||
|
// Endpoint
|
||||||
|
$url = rtrim($baseURL, "/") . "/invoices";
|
||||||
|
|
||||||
|
// Prepare payload
|
||||||
|
$data = [
|
||||||
|
"invoice_number" => $invoiceNumber,
|
||||||
|
"amount" => (int)$amount,
|
||||||
|
"msisdn" => $msisdn,
|
||||||
|
"description" => $description
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer " . $token,
|
||||||
|
"Content-Type: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||||
|
|
||||||
|
// Execute request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
echo "cURL Error: " . curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode and return response
|
||||||
|
return $response;//json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePassword($baseURL, $token,$newPassword, $newPasswordConfirmation) {
|
||||||
|
// Endpoint URL
|
||||||
|
$url = rtrim($baseURL, "/") . "/password";
|
||||||
|
|
||||||
|
// Prepare data
|
||||||
|
$data = [
|
||||||
|
"new_password" => $newPassword,
|
||||||
|
"new_password_confirmation" => $newPasswordConfirmation
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); // PATCH request
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer " . $token,
|
||||||
|
"Content-Type: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||||
|
|
||||||
|
// Execute and capture response
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
echo "cURL Error: " . curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode JSON response
|
||||||
|
return json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate_msisdn($baseURL, $msisdn, $bearerToken)
|
||||||
|
{
|
||||||
|
// Ensure proper endpoint format
|
||||||
|
$url = rtrim($baseURL, '/') . '/payments/validate/' . urlencode($msisdn);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Authorization: Bearer ' . $bearerToken,
|
||||||
|
'Accept: application/json'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Handle cURL error
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get HTTP status code
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode JSON response
|
||||||
|
$result = json_decode($response, true);
|
||||||
|
|
||||||
|
if ($httpCode === 200 && isset($result['data']['full_name'])) {
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'full_name' => $result['data']['full_name']
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => $result['message'] ?? 'Unknown error',
|
||||||
|
'details' => $result['errors'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function authenticate($baseURL, $wallet, $password)
|
||||||
|
{
|
||||||
|
// Endpoint URL
|
||||||
|
$url = rtrim($baseURL, '/') . '/authenticate';
|
||||||
|
|
||||||
|
// JSON payload
|
||||||
|
$postData = json_encode([
|
||||||
|
'wallet' => $wallet,
|
||||||
|
'password' => $password
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
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)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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['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'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
147
mobile_money/mpamba/payments_check.php
Normal file
147
mobile_money/mpamba/payments_check.php
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
$incoming=file_get_contents("php://input");
|
||||||
|
$details=json_decode($incoming,true);
|
||||||
|
|
||||||
|
$transactionId=$details['transactionId'];
|
||||||
|
//$token=$details['token'];
|
||||||
|
|
||||||
|
|
||||||
|
$baseURL="https://devpayouts.tnmmpamba.co.mw/api";
|
||||||
|
|
||||||
|
$wallet="505073";
|
||||||
|
$password="N8O7L0vpl5mflfwHzf4DSle9bToV*";//CEDAR PASSWORD & All other wallets
|
||||||
|
|
||||||
|
$res=authenticate($baseURL, $wallet, $password);
|
||||||
|
|
||||||
|
//echo print_r($res,true);
|
||||||
|
$bearerToken=$res['token'];
|
||||||
|
|
||||||
|
|
||||||
|
$res=checkPaymentStatus($transactionId, $bearerToken, $baseURL);
|
||||||
|
echo(print_r($res,true));
|
||||||
|
|
||||||
|
function checkPaymentStatus($transactionId, $bearerToken, $baseURL)
|
||||||
|
{
|
||||||
|
$url = rtrim($baseURL, '/') . "/payments/" . urlencode($transactionId);
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
|
||||||
|
curl_setopt_array($ch, array(
|
||||||
|
CURLOPT_URL => $url,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_HTTPHEADER => array(
|
||||||
|
"Authorization: Bearer " . $bearerToken,
|
||||||
|
"Accept: application/json"
|
||||||
|
),
|
||||||
|
CURLOPT_TIMEOUT => 30
|
||||||
|
));
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
$error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
"status" => "ERROR",
|
||||||
|
"message" => "cURL error: " . $error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$decoded = json_decode($response, true);
|
||||||
|
|
||||||
|
if ($httpCode !== 200 || !$decoded) {
|
||||||
|
return array(
|
||||||
|
"status" => "FAILED",
|
||||||
|
"http_code" => $httpCode,
|
||||||
|
"raw" => $response
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safely extract data
|
||||||
|
$data = $decoded['data'] ?? [];
|
||||||
|
|
||||||
|
$success = $data['success'] ?? false;
|
||||||
|
$resultCode = $data['result_code'] ?? null;
|
||||||
|
$receipt = $data['receipt_number'] ?? null;
|
||||||
|
|
||||||
|
// Determine final status
|
||||||
|
if ($success === true && $resultCode === "0") {
|
||||||
|
$finalStatus = "SUCCESS";
|
||||||
|
} else {
|
||||||
|
$finalStatus = "PENDING"; // default fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
"status" => $finalStatus,
|
||||||
|
"transaction_id" => $data['transaction_id'] ?? $transactionId,
|
||||||
|
"receipt_number" => $receipt,
|
||||||
|
"result_code" => $resultCode,
|
||||||
|
"result_description" => $data['result_description'] ?? null,
|
||||||
|
"created_at" => $data['created_at'] ?? null,
|
||||||
|
"message" => $decoded['message'] ?? null,
|
||||||
|
"raw_response" => $decoded
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function authenticate($baseURL, $wallet, $password)
|
||||||
|
{
|
||||||
|
$url = rtrim($baseURL, '/') . '/authenticate';
|
||||||
|
|
||||||
|
$postData = json_encode([
|
||||||
|
'wallet' => $wallet,
|
||||||
|
'password' => $password
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$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)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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['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'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
216
mobile_money/mpamba/testing.php
Normal file
216
mobile_money/mpamba/testing.php
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
<?php
|
||||||
|
$incoming=file_get_contents("php://input");
|
||||||
|
$details=json_decode($incoming,true);
|
||||||
|
|
||||||
|
$invoiceNumber=$details['invoiceNumber'];
|
||||||
|
$amount=$details['amount'];
|
||||||
|
$msisdn=$details['msisdn'];
|
||||||
|
$description=$details['description'];
|
||||||
|
|
||||||
|
|
||||||
|
$baseURL="https://devpayouts.tnmmpamba.co.mw/api";
|
||||||
|
$wallet="505072";
|
||||||
|
$password="N8O7L0vpl5mflfwHzf4DSle9bToV*";//CEDAR PASSWORD & All other wallets
|
||||||
|
|
||||||
|
$res=authenticate($baseURL, $wallet, $password);
|
||||||
|
|
||||||
|
echo print_r($res,true);
|
||||||
|
$bearerToken=$res['token'];//"102164|newoEd6QOFaTptUiGAp232jULtUmgMtaX1x2CRww4Ka2270dc49";
|
||||||
|
|
||||||
|
echo $bearerToken;
|
||||||
|
|
||||||
|
//$res=validate_msisdn($baseURL, '265881161942', $bearerToken);
|
||||||
|
//$res=changePassword($baseURL, $bearerToken, "N8O7L0vpl5mflfwHzf4DSle9bToV*", "N8O7L0vpl5mflfwHzf4DSle9bToV*");
|
||||||
|
$res=sendUSSDPush($baseURL, $bearerToken, $invoiceNumber, $amount, $msisdn, $description);
|
||||||
|
echo (print_r($res,true));
|
||||||
|
|
||||||
|
function sendUSSDPush($baseURL, $token, $invoiceNumber, $amount, $msisdn, $description) {
|
||||||
|
// Endpoint
|
||||||
|
$url = rtrim($baseURL, "/") . "/invoices";
|
||||||
|
|
||||||
|
// Prepare payload
|
||||||
|
$data = [
|
||||||
|
"invoice_number" => $invoiceNumber,
|
||||||
|
"amount" => (int)$amount,
|
||||||
|
"msisdn" => $msisdn,
|
||||||
|
"description" => $description
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer " . $token,
|
||||||
|
"Content-Type: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||||
|
|
||||||
|
// Execute request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
echo "cURL Error: " . curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode and return response
|
||||||
|
return $response;//json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePassword($baseURL, $token,$newPassword, $newPasswordConfirmation) {
|
||||||
|
// Endpoint URL
|
||||||
|
$url = rtrim($baseURL, "/") . "/password";
|
||||||
|
|
||||||
|
// Prepare data
|
||||||
|
$data = [
|
||||||
|
"new_password" => $newPassword,
|
||||||
|
"new_password_confirmation" => $newPasswordConfirmation
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); // PATCH request
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer " . $token,
|
||||||
|
"Content-Type: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||||
|
|
||||||
|
// Execute and capture response
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
echo "cURL Error: " . curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode JSON response
|
||||||
|
return json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate_msisdn($baseURL, $msisdn, $bearerToken)
|
||||||
|
{
|
||||||
|
// Ensure proper endpoint format
|
||||||
|
$url = rtrim($baseURL, '/') . '/payments/validate/' . urlencode($msisdn);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Authorization: Bearer ' . $bearerToken,
|
||||||
|
'Accept: application/json'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Handle cURL error
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get HTTP status code
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Decode JSON response
|
||||||
|
$result = json_decode($response, true);
|
||||||
|
|
||||||
|
if ($httpCode === 200 && isset($result['data']['full_name'])) {
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'full_name' => $result['data']['full_name']
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => $result['message'] ?? 'Unknown error',
|
||||||
|
'details' => $result['errors'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function authenticate($baseURL, $wallet, $password)
|
||||||
|
{
|
||||||
|
// Endpoint URL
|
||||||
|
$url = rtrim($baseURL, '/') . '/authenticate';
|
||||||
|
|
||||||
|
// JSON payload
|
||||||
|
$postData = json_encode([
|
||||||
|
'wallet' => $wallet,
|
||||||
|
'password' => $password
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init($url);
|
||||||
|
|
||||||
|
// Set cURL options
|
||||||
|
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)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
curl_close($ch);
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'Curl error: ' . curl_error($ch)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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['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'] ?? []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
60
mobile_money/mpamba/transaction_check.php
Normal file
60
mobile_money/mpamba/transaction_check.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
$incoming=file_get_contents("php://input");
|
||||||
|
$details=json_decode($incoming,true);
|
||||||
|
|
||||||
|
$invoiceNumber=$details['invoiceNumber'];
|
||||||
|
$token=$details['token'];
|
||||||
|
|
||||||
|
|
||||||
|
$baseURL="https://devpayouts.tnmmpamba.co.mw/api";
|
||||||
|
|
||||||
|
//$res=authenticate($baseURL, $wallet, $password);
|
||||||
|
$bearerToken=$token;//"102164|newoEd6QOFaTptUiGAp232jULtUmgMtaX1x2CRww4Ka2270dc49";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$res=checkInvoiceStatus($invoiceNumber, $bearerToken, $baseURL);
|
||||||
|
echo $res;//(print_r($res,true));
|
||||||
|
|
||||||
|
function checkInvoiceStatus($invoiceNumber, $bearerToken, $baseURL) {
|
||||||
|
// Build request URL
|
||||||
|
$url = rtrim($baseURL, '/') . "/invoices/" . urlencode($invoiceNumber);
|
||||||
|
|
||||||
|
// Initialize cURL
|
||||||
|
$ch = curl_init();
|
||||||
|
|
||||||
|
curl_setopt_array($ch, [
|
||||||
|
CURLOPT_URL => $url,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_HTTPHEADER => [
|
||||||
|
"Authorization: Bearer " . $bearerToken,
|
||||||
|
"Accept: application/json"
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
// Handle errors
|
||||||
|
if (curl_errno($ch)) {
|
||||||
|
echo "cURL Error: " . curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Check for successful response
|
||||||
|
if ($httpCode === 200 && $response) {
|
||||||
|
return $response;//json_decode($response, true);
|
||||||
|
} else {
|
||||||
|
echo "Request failed. HTTP Code: " . $httpCode . "\nResponse: " . $response;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
14
public/.htaccess
Normal file
14
public/.htaccess
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
#RewriteBase /mse_api/public
|
||||||
|
|
||||||
|
# 1. Prevent directory browsing
|
||||||
|
Options -Indexes
|
||||||
|
|
||||||
|
# 2. If the request is NOT a real directory...
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
# 3. ...and is NOT a real file...
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
# 4. ...send the request to index.php
|
||||||
|
RewriteRule ^ index.php [L]
|
||||||
30
public/index.php
Normal file
30
public/index.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
$dotenv->required(['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS'])->notEmpty();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
use App\Core\Router;
|
||||||
|
|
||||||
|
|
||||||
|
$router = new Router();
|
||||||
|
|
||||||
|
|
||||||
|
$router->get('/', 'HomeController@index');
|
||||||
|
$router->post('/disbursement', 'DisbursementController@index');
|
||||||
|
$router->get('/test', 'DisbursementController@testUpdate');
|
||||||
|
|
||||||
|
$request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||||
|
$base_path = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));
|
||||||
|
if ($base_path !== '/' && strpos($request_uri, $base_path) === 0) {
|
||||||
|
$request_uri = substr($request_uri, strlen($base_path));
|
||||||
|
}
|
||||||
|
$request_uri = '/' . ltrim($request_uri, '/');
|
||||||
|
|
||||||
|
$router->resolve($request_uri, $_SERVER['REQUEST_METHOD']);
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
Reference in New Issue
Block a user