Initial commit

This commit is contained in:
Kwesi Banson Jnr
2026-08-04 14:50:01 +00:00
commit 2cfcfade4e
1605 changed files with 499334 additions and 0 deletions

40
.gitignore vendored Normal file
View File

@@ -0,0 +1,40 @@
docker-compose.yml
Dockerfile
.DS_Store
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
/vendor
/node_modules
/public/hot
/public/storage
/storage
/bootstrap/cache
/.env
.env.backup
.phpunit.result.cache
Homestead.yaml
Homestead.json
/.vagrant
npm-debug.log
yarn-error.log
.idea
.vscode

28
.htaccess Normal file
View File

@@ -0,0 +1,28 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
<Files ~ "^.*\.([Ee][Nn][Vv])">
order allow,deny
deny from all
satisfy all
</Files>
php_value upload_max_filesize 35M
php_value post_max_size 50M

64
README.md Normal file
View File

@@ -0,0 +1,64 @@
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400"></a></p>
<p align="center">
<a href="https://travis-ci.org/laravel/framework"><img src="https://travis-ci.org/laravel/framework.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
## About Laravel
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
Laravel is accessible, powerful, and provides tools required for large, robust applications.
## Learning Laravel
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 1500 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
## Laravel Sponsors
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
### Premium Partners
- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Cubet Techno Labs](https://cubettech.com)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[Many](https://www.many.co.uk)**
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
- **[DevSquad](https://devsquad.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
- **[OP.GG](https://op.gg)**
- **[WebReinvent](https://webreinvent.com/?utm_source=laravel&utm_medium=github&utm_campaign=patreon-sponsors)**
- **[Lendio](https://lendio.com)**
## Contributing
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
## License
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

45
accounts.md Normal file
View File

@@ -0,0 +1,45 @@
# URL
- 206.225.87.174:8131
- http://206.225.87.174:8131/admin/users
## Super Admin
- kwesi@click-mobile.com/password
- martha@routemobility.com/karDaj-rerdes-7zucze
- kwamina@click-mobile.com/ninmqw-7zucze-rerdes
## Dealer
- kwesi@click-mobile.com
- password
## Martha dealer
- martha/nyVMpCwR
Your organisation (Martha Mwamba) has been successfully registered as an Airtime Sales Dealer.
Your Password is nyVMpCwR. You will need it for all your activities.
## David Merchant
- 265881161942
- 1234
## Martha
- 260967080547
- 2003
- 260 954686068
- 2003
## API Token (Kwesi) .174:8131
{
"user": {
"id": 1,
"name": "Kwesi Banson Jnr",
"email": "kwesi@click-mobile.com",
"email_verified_at": null,
"created_at": "2020-06-11T15:50:55.000000Z",
"updated_at": "2022-03-09T12:16:49.000000Z",
"status": "Active"
},
"token": "1|d0HIst2IFzS6FA4HYS4eqLooMQWVFpzHTzHvTPkd"
}

24
app/ActivityLog.php Executable file
View File

@@ -0,0 +1,24 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ActivityLog extends Model
{
//
protected $guarded = ['id'];
public function admin()
{
return $this->belongsTo('App\User', 'user_id', 'id');
}
public function merchantInfo()
{
return $this->belongsTo('App\Models\Merchant', 'merchant_id', 'id');
}
public function orgUserInfo()
{
return $this->belongsTo('App\Models\OrgUser', 'org_user_id', 'id');
}
}

32
app/Console/Kernel.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
use Illuminate\Auth\AuthenticationException;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson() || str_starts_with($request->path(), 'api/')) {
return response()->json(['message' => $exception->getMessage()], 401);
}
parent::unauthenticated($request, $exception);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\ActivityLog;
class ActivityController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
$activities = ActivityLog::with('admin')->where('user_type', 'admin')->orderBy('created_at', 'desc')->paginate(30);
$data = [
'activities' => $activities,
'page_title' => 'Admin Activity Log'
];
return view('activity.index', $data);
}
}

View File

@@ -0,0 +1,383 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models;
use Session;
use DB;
use Illuminate\Support\Facades\Hash;
use App\ActivityLog;
use App\Libs\Smsgateway;
class AdminController extends Controller
{
public function index(){
$dealers = Models\OrgUser::latest()->paginate(20);
$data = [
'organisations' => $dealers,
'page_title' => 'Dealers'
];
return view('super_admin.organisations',$data);
}
public function merchant_index(){
$merchants = Models\Merchant::paginate(20);
$data = [
'merchants' => $merchants,
'page_title' => 'Merchants'
];
return view('super_admin.merchants',$data);
}
public function createOrg(){ //create a dealer
$data = [
'page_title' => 'New Dealer'
];
return view('super_admin.create-org', $data);
}
/**
* Store a newly created merchant in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request){ // save merchant in the DB
$this->validate($request, [
'fullname' => 'required',
'phone_number'=> 'required|unique:merchants,phone',
'language' => 'required'
]);
$check_phone = $this->validatePhoneNumber($request->phone_number);
if ($check_phone == false) {
return redirect()->back()->withInput()->with('admin-error', 'Invalid phone number!');
}
$fullname = $request->input('fullname');
$phone_number = $request->input('phone_number');
$pin = rand(1000,9999);
$merchant = Models\Merchant::create([
'fullname' => $fullname,
'phone' => $check_phone,
'pin' => md5($pin),
'language' => $request->language
]);
$desc = [
'title' => 'Created a merchant',
'data' => $fullname,
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = Auth::user()->id;
$activity->user_type = 'admin';
$activity->description = json_encode($desc);
$activity->save();
// TODO: send sms with PIN to merchant phone -
$msg = "You have been Successfully registered as an Airtime sales merchant. Your 4-digit PIN is $pin. You wull need it for all your activities. Dial *349# to begin.";
$smsgateway = new Smsgateway;
if (env('APP_ENV') != 'local') {
$result = $smsgateway->sendSmsNew($check_phone, $msg);
}
// $result = $smsgateway->sendSmsNew($check_phone, $msg);
\Log::info($msg);
return redirect()->to('/admin/merchant')->with('admin-sucsess', 'Successfully Added New Merchant');
}
public function organisationStore(Request $request){
$this->validate($request, [
'user_phone' => 'required',
'user_email' => 'required',
'user_name' => 'required',
'country' => 'required',
'currency' => 'required'
]);
$generated_password = $this->randomPassword();
$org_user_results = Models\OrgUser::create([
'name' => $request->user_name,
'phone' => $request->user_phone,
'email' => $request->user_email,
'country' => $request->country,
'currency' => $request->currency,
'added_by' => \Auth::user()->name,
'password' => Hash::make($generated_password),
]);
$desc = [
'title' => 'New Dealer',
'data' => \Auth::user()->name . ' added a new dealer with name ' . $request->name,
'type' => 'store',
];
$activity = new ActivityLog;
$activity->user_id = \Auth::user()->id;
$activity->user_type = 'admin';
$activity->description = json_encode($desc);
$activity->save();
$login_url = env('MAIN_WEB_URL') . "/organisation";
// TODO: send sms with Password to merchant phone -
$msg = "Hello ($request->name), you have successfully been registered as an Airtime Sales Dealer. Your Password is $generated_password. You will need it for all your activities. \r\nLogin Here : $login_url";
$smsgateway = new Smsgateway;
if (env('APP_ENV') != 'local') {
$result = $smsgateway->sendSmsNew($check_user_phone, $msg);
}
\Log::info($msg);
Session::flash('success_message', 'Dealer Successfully added!');
return redirect()->to('/admin/organisations');
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
$dealer = Models\OrgUser::findOrfail($id);
if (!$dealer) {
return redirect()->back()->with('admin-error', 'Dealer not found!');
}
// check if the user is allowed to edit this organisation
$data = [
'organisation' => $dealer,
'page_title' => 'Edit Dealer',
];
return view('super_admin.organisation_edit',$data);
}
public function show($id)
{
//
$dealer = Models\OrgUser::findOrfail($id);
if (!$dealer) {
return redirect()->back()->with('admin-error', 'Dealer not found!');
}
// check if the user is allowed to edit this organisation
$data = [
'organisation' => $dealer,
'page_title' => 'Dealer Details',
];
return view('super_admin.organisation_show', $data);
}
public function merchant_edit($id){
$merchant = Models\Merchant::findOrfail($id);
if (!$merchant) {
return redirect()->back()->with('admin-error', 'Merchant not found!');
}
$data = [
'merchant' => $merchant,
'page_title' => 'Edit Merchant Details',
];
return view('super_admin.merchant_edit',$data);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->validate($request, [
'name' => 'required',
'email' => 'required',
'country' => 'required',
'currency' => 'required',
'phone' => [
'required',
'string',
'regex:/^\+?[0-9\s\-\(\)]{7,15}$/', // Allows optional leading '+' and 7 to 15 digits/formatting characters
],
]);
// dd($request->all());
Models\OrgUser::where('id', $id)->update([
'name' => $request->name,
'phone' => $request->phone,
'email' => $request->email,
'last_modified_user_id' => \Auth::user()->id,
'status' => $request->status,
]);
$desc = [
'title' =>'Updated Dealer details',
'data' => $request->all(),
'type' => 'update',
];
$activity = new ActivityLog;
$activity->user_id = \Auth::user()->id; //session('current_org_user.id');// organisation user ID
$activity->user_type = 'super_admin';
$activity->description = json_encode($desc);
$activity->save();
// dd('Activity logged');
return redirect()->to('admin/organisations')->with('admin-success', 'Successfully Updated Dealer details');
}
public function merchant_update(Request $request, $id)
{
$this->validate($request, [
'fullname' => 'required',
'phone'=> 'required',
// 'email' => 'required',
'language' => 'required',
'status' => 'required',
// 'user_name' => 'required',
]);
$check_phone = $this->validatePhoneNumber($request->phone);
if ($check_phone == false) {
return redirect()->back()->withInput()->with('admin-error', 'Invalid phone number!');
}
// dd($request->all());
Models\Merchant::where('id', $id)->update([
'fullname' => $request->fullname,
'phone' => $check_phone,
'language' => $request->language,
// 'last_modified_user_id' => \Auth::user()->id,
'status' => $request->status,
// 'user_email' => $request->user_email,
// 'user_name' => $request->user_name
]);
$desc = [
'title' =>'Updated Dealer details',
'data' => $request->all(),
'type' => 'Merchant',
];
$activity = new ActivityLog;
$activity->user_id = \Auth::user()->id; //session('current_org_user.id');// organisation user ID
$activity->user_type = 'merchant';
$activity->description = json_encode($desc);
$activity->save();
// dd('Activity logged');
return redirect()->to('admin/merchants')->with('admin-success', 'Successfully Updated merchant details');
}
public function getMerchantSales(){
// sales from merchants to end users
$merchant_sales = Models\Transaction::with('merchant')->latest()->paginate(30);
$data = [
'merchant_sales' => $merchant_sales,
'page_title' => 'Merchant Sales'
];
return view('super_admin.merchant_sales', $data);
}
public function getDealerSales(){
// sales from dealers to merchants
$dealer_sales = Models\MerchantTopUp::with('merchantInfo', 'orgInfo')->latest()->paginate(20);
// dd($dealer_sales);
$data = [
'dealer_sales' => $dealer_sales,
'page_title' => 'Dealer Sales'
];
return view('super_admin.dealer_sales', $data);
}
public function AdminTransactions(){
$data = [
'page_title' => 'Admin Transactions'
];
return view('super_admin.admin-transactions', $data);
}
public function getTransactionsJson(Request $request)
{
//$this->log_query();
$transactions_arr = \DB::table('merchant_top_ups')
->join('org_users', 'merchant_top_ups.org_user_id', '=', 'org_users.id')
->select('merchant_top_ups.id', 'merchant_top_ups.status', 'org_users.name', 'merchant_top_ups.payment_method', 'merchant_top_ups.org_user_id', 'merchant_top_ups.amount', 'merchant_top_ups.created_at')
->orderBy('merchant_top_ups.created_at', 'DESC')
->paginate(10);
if($request->has('keyword')){
$queries = [];
$keyword = $request->keyword;
$queries['keyword'] = $keyword;
$transactions_arr = \DB::table('merchant_top_ups')
->join('org_users', 'merchant_top_ups.org_user_id', '=', 'org_users.id')
->select('merchant_top_ups.id', 'merchant_top_ups.status', 'org_users.name', 'merchant_top_ups.payment_method', 'merchant_top_ups.org_user_id', 'merchant_top_ups.amount', 'merchant_top_ups.created_at')
->whereRaw("org_user.name LIKE '%$keyword%' OR merchant_top_ups.status LIKE '%$keyword%' OR merchant_top_ups.payment_method LIKE '%$keyword%' OR merchant_top_ups.amount LIKE '%$keyword%' OR merchant_top_ups.created_at LIKE '%$keyword%'")
->orderBy('merchant_top_ups.created_at', 'DESC')
->paginate(10)->appends($queries);
}
return response()->json($transactions_arr);
}
public function getAdminTransactionsJson(Request $request)
{
$query = \DB::table('organisation_top_ups')
->join('users', 'users.id', '=', 'organisation_top_ups.user_id')
->join('organisations', 'organisations.id', '=', 'organisation_top_ups.org_id')
->select(
'organisation_top_ups.id', 'organisation_top_ups.current_balance', 'organisations.balance',
'organisation_top_ups.amount', 'organisation_top_ups.status', 'users.name AS admin_name',
'organisation_top_ups.created_at', 'users.name', 'organisations.name AS dealer_name'
);
if ($request->has('filter')) {
$filters = $request->input('filter');
// Map the Tabulator JS fields to the actual database table columns
$columnMap = [
'admin_name' => 'users.name',
'balance' => 'organisations.balance',
'current_balance' => 'organisation_top_ups.current_balance',
'amount' => 'organisation_top_ups.amount',
'dealer_name' => 'organisations.name'
];
foreach ($filters as $filter) {
$field = $filter['field'];
$value = $filter['value'];
// Determine the correct DB column. Default to sender_ids table to avoid 'ambiguous column' errors
$dbColumn = $columnMap[$field] ?? 'organisation_top_ups.' . $field;
// Securely bind the value using Laravel's active record (prevents SQL injection)
$query->where($dbColumn, 'LIKE', '%' . $value . '%');
}
}
$query->orderBy('organisation_top_ups.created_at', 'DESC');
$perPage = $request->input('size', 15);
$senderid_arr = $query->paginate($perPage);
// If using the global keyword, append it to the pagination links
if ($request->has('keyword')) {
$senderid_arr->appends(['keyword' => $request->keyword]);
}
return response()->json($senderid_arr);
}
public function randomPassword() {
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
$pass = array(); //remember to declare $pass as an array
$alphaLength = strlen($alphabet) - 1; //put the length -1 in cache
for ($i = 0; $i < 8; $i++) {
$n = rand(0, $alphaLength);
$pass[] = $alphabet[$n];
}
return implode($pass); //turn the array into a string
}
}

View File

@@ -0,0 +1,313 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
// use Illuminate\Support\Facades\Validator;
use Validator;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use App\Models;
use App\ActivityLog;
use App\Libs\Smsgateway;
class ApiDealersController extends Controller
{
public function handleLogin(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required'
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Email and password are required.',
'errors' => $validator->errors()
], 422);
}
$org_user = Models\OrgUser::where('email', $request->email)->first();
if (!$org_user || !Hash::check($request->password, $org_user->password)) {
return response()->json([
'status' => 'error',
'message' => 'Email or password incorrect.'
], 401);
}
$desc = [
'title' => 'Login',
'data' => $org_user->name . ' Successful login'
];
$activity = new ActivityLog();
$activity->user_id = $org_user->id;
$activity->user_type = 'organisation';
$activity->description = json_encode($desc);
$activity->save();
return response()->json([
'status' => 'success',
'message' => 'Login successful.',
'data' => [
'user' => $org_user,
]
]);
}
public function getBalance(Request $request)
{
$validator = Validator::make($request->all(), [
'dealer_id' => 'required|integer|exists:org_users,id'
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error.',
'errors' => $validator->errors()
], 422);
}
$dealer_id = $request->dealer_id;
$org_user = Models\OrgUser::find($dealer_id);
if (!$org_user || !Hash::check($request->password, $org_user->password)) {
return response()->json([
'status' => 'error',
'message' => 'Email or password incorrect.'
], 401);
}
$desc = [
'title' => 'Login',
'data' => $org_user->name . ' Successful login'
];
$activity = new ActivityLog();
$activity->user_id = $org_user->id;
$activity->user_type = 'organisation';
$activity->description = json_encode($desc);
$activity->save();
return response()->json([
'status' => 'success',
'message' => 'Balance check successful',
'data' => [
'balance' => $org_user->balance,
]
]);
}
public function getTransactionsJson(Request $request)
{
$validator = Validator::make($request->all(), [
'dealer_id' => 'required|integer|exists:org_users,id'
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error.',
'errors' => $validator->errors()
], 422);
}
$dealer_id = $request->dealer_id;
$query = DB::table('merchant_top_ups')
->join('org_users', 'merchant_top_ups.org_user_id', '=', 'org_users.id')
->join('merchants', 'merchant_top_ups.merchant_id', '=', 'merchants.id')
->select('merchant_top_ups.id', 'merchant_top_ups.status', 'merchants.phone', 'merchants.fullname', 'merchant_top_ups.payment_method', 'merchant_top_ups.org_user_id', 'merchant_top_ups.amount', 'merchant_top_ups.created_at')
->where('merchant_top_ups.org_user_id', $dealer_id);
if ($request->has('keyword')) {
$keyword = $request->keyword;
$query->where(function($q) use ($keyword) {
$q->where('merchants.fullname', 'LIKE', "%{$keyword}%")
->orWhere('merchants.phone', 'LIKE', "%{$keyword}%")
->orWhere('merchant_top_ups.status', 'LIKE', "%{$keyword}%")
->orWhere('merchant_top_ups.payment_method', 'LIKE', "%{$keyword}%")
->orWhere('merchant_top_ups.amount', 'LIKE', "%{$keyword}%")
->orWhere('merchant_top_ups.created_at', 'LIKE', "%{$keyword}%");
});
}
$transactions_arr = $query->orderBy('merchant_top_ups.created_at', 'DESC')->paginate(10);
return response()->json([
'status' => 'success',
'data' => $transactions_arr
]);
}
public function getMyTopUpsJson(Request $request)
{
$validator = Validator::make($request->all(), [
'dealer_id' => 'required|integer|exists:org_users,id'
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error.',
'errors' => $validator->errors()
], 422);
}
$dealer_id = $request->dealer_id;
$query = DB::table('organisation_top_ups')
->join('org_users', 'organisation_top_ups.org_id', '=', 'org_users.org_id')
->join('users', 'organisation_top_ups.user_id', '=', 'users.id')
->select('organisation_top_ups.id', 'organisation_top_ups.status', 'org_users.phone', 'users.name', 'organisation_top_ups.payment_method', 'organisation_top_ups.org_id', 'organisation_top_ups.amount', 'organisation_top_ups.created_at')
->where('organisation_top_ups.org_user_id', $dealer_id);
if ($request->has('keyword')) {
$keyword = $request->keyword;
$query->where(function($q) use ($keyword) {
$q->where('org_users.name', 'LIKE', "%{$keyword}%")
->orWhere('org_users.phone', 'LIKE', "%{$keyword}%")
->orWhere('organisation_top_ups.status', 'LIKE', "%{$keyword}%")
->orWhere('organisation_top_ups.payment_method', 'LIKE', "%{$keyword}%")
->orWhere('organisation_top_ups.amount', 'LIKE', "%{$keyword}%")
->orWhere('organisation_top_ups.created_at', 'LIKE', "%{$keyword}%");
});
}
$transactions_arr = $query->orderBy('organisation_top_ups.created_at', 'DESC')->paginate(10);
return response()->json([
'status' => 'success',
'data' => $transactions_arr
]);
}
public function merchantTopupStore(Request $request)
{
$validator = Validator::make($request->all(), [
'merchant_id' => 'required|integer|exists:merchants,id',
'amount' => 'required|numeric|min:1',
'dealer_id' => 'required|integer|exists:org_users,id'
]);
if ($validator->fails()) {
return response()->json([
'code' => 3,
'status' => 'failed',
'message' => 'Validation error.',
'errors' => $validator->errors()
], 422);
}
$dealer_id = $request->dealer_id;
$merchant_id = $request->merchant_id;
$amount = (float) $request->amount;
$dealer = Models\OrgUser::findOrFail($dealer_id);
$merchant = Models\Merchant::findOrFail($merchant_id);
if ($amount > $dealer->balance) {
return response()->json([
'code' => 3,
'status' => 'failed',
'message' => 'Insufficient Balance.',
'balance' => $dealer->balance
], 400);
}
DB::beginTransaction();
try {
Models\MerchantTopUp::create([
'merchant_id' => $merchant_id,
'amount' => $amount,
'transaction_id' => 'MCH-' . Str::uuid(),
'org_user_id' => $dealer_id,
'created_by' => $dealer_id,
'payment_method' => 'cash'
]);
$merchant->increment('balance', $amount);
$dealer->decrement('balance', $amount);
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
\Log::error($e);
return response()->json([
'code' => 3,
'status' => 'failed',
'message' => 'Merchant Top Up failed.'
], 500);
}
ActivityLog::create([
'user_id' => $dealer_id,
'user_type' => 'Dealer',
'description' => json_encode([
'title' => 'Dealer to Merchant Top Up',
'data' => 'MWK ' . number_format($amount, 2) . ' for ' . $merchant->fullname,
'type' => 'dealer topup'
])
]);
$merchant->refresh();
try {
if (!app()->environment('local')) {
(new Smsgateway())->sendSmsNew(
$merchant->phone,
"Hello {$merchant->fullname}, your account has been credited with MWK {$amount}. Your new balance is MWK {$merchant->balance}."
);
}
} catch (\Exception $e) {
\Log::error($e);
}
return response()->json([
'code' => 1,
'status' => 'success',
'message' => 'Merchant Top Up Successfully completed.'
]);
}
public function getActivity(Request $request)
{
$validator = Validator::make($request->all(), [
'dealer_id' => 'required|integer|exists:org_users,id'
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error.',
'errors' => $validator->errors()
], 422);
}
$dealer_id = $request->dealer_id;
$activities = ActivityLog::with('admin')
->where('user_type', 'organisation')
->where('user_id', $dealer_id)
->orderBy('created_at', 'desc')
->paginate(20);
return response()->json([
'status' => 'success',
'data' => $activities
]);
}
}
///http://206.225.87.174:8131/dealer/topupmerchant

View File

@@ -0,0 +1,552 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Models\Merchant;
use App\Models\Transaction;
use App\ActivityLog;
use App\Libs\Smsgateway;
class ApiMerchantsController extends Controller{
public function merchant_login(Request $request){
$this->validate($request, [
'phone_number' => 'required',
'pin' => 'required'
]);
$phone_number = $this->validatePhoneNumber($request->phone_number);
if ($phone_number === false) {
return response()->json([
'success' => false,
'message' => 'Phone Number/PIN is incorrect!'
], 401);
}
$pin = md5($request->input('pin'));
$merchant = Models\Merchant::where('phone', $phone_number)->where('pin', $pin)->first();
if ($merchant === null) {
return response()->json([
'success' => false,
'message' => 'Phone Number/PIN incorrect!'
], 401);
}
return response()->json([
'success' => true,
'message' => 'Login successful',
'merchant' => [
'id' => $merchant->id,
'phone' => $merchant->phone,
'balance' => $merchant->balance,
'status' => $merchant->status,
'fullname' => $merchant->fullname,
'country' => $merchant->country
]
], 200);
}
public function get_transactions(Request $request)
{
// POST body params: merchant_id (required), keyword (optional), start_date, end_date, per_page
$merchantId = $request->input('merchant_id');
if (!$merchantId) {
return response()->json(['success' => false, 'message' => 'merchant_id is required'], 422);
}
$merchant = Models\Merchant::find($merchantId);
if (!$merchant) {
return response()->json(['success' => false, 'message' => 'merchant not found'], 404);
}
$perPage = (int) $request->input('per_page', 20);
$query = \DB::table('transactions')
->join('merchants', 'transactions.merchant_id', '=', 'merchants.id')
->select(
'merchants.fullname',
'transactions.id',
'transactions.status',
'transactions.msisdn',
'transactions.amount',
'transactions.created_at'
)
->where('transactions.merchant_id', $merchantId);
if ($request->filled('keyword')) {
$keyword = $request->input('keyword');
$like = "%{$keyword}%";
$query->where(function ($q) use ($like) {
$q->where('merchants.fullname', 'like', $like)
->orWhere('transactions.msisdn', 'like', $like)
->orWhere('transactions.status', 'like', $like)
->orWhere('transactions.amount', 'like', $like)
->orWhere('transactions.created_at', 'like', $like);
});
}
if ($request->filled('start_date') || $request->filled('end_date')) {
try {
$start = $request->filled('start_date') ? \Carbon\Carbon::parse($request->input('start_date'))->startOfDay() : null;
$end = $request->filled('end_date') ? \Carbon\Carbon::parse($request->input('end_date'))->endOfDay() : null;
} catch (\Exception $e) {
return response()->json(['success' => false, 'message' => 'Invalid date format'], 422);
}
if ($start && $end) {
$query->whereBetween('transactions.created_at', [$start, $end]);
} elseif ($start) {
$query->where('transactions.created_at', '>=', $start);
} else {
$query->where('transactions.created_at', '<=', $end);
}
}
$query->orderBy('transactions.created_at', 'desc');
$paginator = $query->paginate($perPage)->appends($request->except('page'));
return response()->json($paginator);
}
public function get_dealers(Request $request){
$perPage = (int) $request->input('per_page', 20);
#$dealer_arr = \DB::table('organisations')->orderBy('organisations.created_at', 'DESC')->paginate(20);
$query = \DB::table('organisations')->select('organisations.id', 'organisations.name', 'organisations.email', 'organisations.phone', 'organisations.status');
if ($request->filled('keyword')) {
$keyword = $request->input('keyword');
$like = "%{$keyword}%";
$query->where(function ($q) use ($like) {
$q->where('organisations.name', 'like', $like)
->orWhere('organisations.phone', 'like', $like)
->orWhere('organisations.email', 'like', $like)
->orWhere('organisations.status', 'like', $like)
->orWhere('organisations.created_at', 'like', $like);
});
}
$paginator = $query->paginate($perPage)->appends($request->except('page'));
return response()->json($paginator);
}
public function getDealerTransactionsJson(Request $request){
$merchant_id = session('merchant.id');
// dd($merchant_id);
$transactions_arr = \DB::table('merchant_top_ups')
->join('merchants', 'merchant_top_ups.merchant_id', '=', 'merchants.id')
->join('org_users', 'merchant_top_ups.org_user_id', '=', 'org_users.id')
->select('merchants.fullname', 'org_users.phone', 'org_users.name', 'merchant_top_ups.id', 'merchant_top_ups.status', 'merchant_top_ups.amount', 'merchant_top_ups.created_at')
->whereRaw('merchant_top_ups.merchant_id = '. $merchant_id)
->orderBy('merchant_top_ups.created_at', 'DESC')
->paginate(20);
if($request->has('keyword')){
$queries = [];
$keyword = $request->keyword;
$queries['keyword'] = $keyword;
$transactions_arr = \DB::table('merchant_top_ups')
->join('merchants', 'merchant_top_ups.merchant_id', '=', 'merchants.id')
->join('org_users', 'merchant_top_ups.org_user_id', '=', 'org_users.id')
->select('merchants.fullname', 'org_users.phone', 'org_users.name', 'merchant_top_ups.id', 'merchant_top_ups.status', 'merchant_top_ups.amount', 'merchant_top_ups.created_at')
->whereRaw("org_users.phone LIKE '%$keyword%' OR org_users.name LIKE '%$keyword%' OR merchants.fullname LIKE '%$keyword%' OR merchant_top_ups.status LIKE '%$keyword%' OR merchant_top_ups.amount LIKE '%$keyword%' OR merchant_top_ups.created_at LIKE '%$keyword%'")
->whereRaw('merchant_top_ups.merchant_id = '. $merchant_id)
->orderBy('merchant_top_ups.created_at', 'DESC')
->paginate(20)->appends($queries);
}
return response()->json($transactions_arr);
}
public function merchant_transactions($merchant){
$transactions = Models\Transaction::where('merchant_id', $merchant->id)
->select('id', 'created_at')->get()
->groupBy(function($date) {
return Carbon::parse($date->created_at)->format('m'); // grouping by months
});
$transactionmcount = [];
$transactionArr = [];
$pp = 1;
$trans = [];
foreach ($transactions as $value) {
if ($pp == $value) {
$trans[$pp]= $value;
}
else{
$trans[$pp] = 0;
}
}
foreach ($transactions as $key => $value) {
$transactionmcount[(int)$key] = count($value);
}
for($i = 1; $i <= 12; $i++){
if(!empty($transactionmcount[$i])){
$transactionArr[$i] = $transactionmcount[$i];
}else{
$transactionArr[$i] = 0;
}
}
$transactions = '';
foreach ($transactionArr as $tran) {
$transactions = $transactions.','.$tran;
}
return response()->json(['transactions' => $transactions]);
}
public function customerTopupStoreOld(Request $request){
$this->validate($request, [
'phone_number'=> 'required|numeric',
'amount' => 'required|numeric|min:1',
'merchant_id' => 'required|numeric'
]);
$phone_number = preg_replace('/\s+/', '', $request->phone_number);
$check_phone = $this->validatePhoneNumber($phone_number);
if ($check_phone == false) {
return response()->json([
'success' => false,
'message' => 'Invalid phone number!'
], 401);
}
$merchant = Models\Merchant::with('orgInfo')->find($request->merchant_id);
if ($merchant == false) {
return response()->json([
'success' => false,
'message' => 'merchant not found!'
], 401);
}
$difference = $merchant->balance - $request->amount;
if ($merchant->balance < $request->amount) {
return response()->json([
'success' => false,
'message' => 'Insufficient balance!'
], 401);
}
$prev_balance = $merchant->balance;
$extRefId = uniqid() . '-' . date('YmdHis');
$transaction_arr = [
'msisdn' => $check_phone,
'amount' => $request->amount,
'merchant_id' => $merchant->id,
'session_id' => $extRefId
];
// dd($transaction_arr);
$trans_result = Models\Transaction::create($transaction_arr);
$transaction_id = $trans_result->id;
$click_params = [
"msisdn" => $check_phone,
"amount" => $request->amount,
"extRefId" => $extRefId
];
\Log::info($click_params);
$topup_result = $this->ClickADPHttp($click_params);
\Log::info($topup_result);
$this->sendNtfy($topup_result);
$topup_result_arr = json_decode($topup_result, TRUE);
\Log::info($topup_result_arr);
if ($topup_result == false) {
return response()->json([
'success' => false,
'message' => 'Top Up Could not be processed at this time. Try again later.'
], 401);
}
$log_data = [
'msisdn' => $check_phone,
'amount' => $request->tamount,
'user_id' => $merchant->id,
'merchant_balance' => $merchant->balance,
'session_id' => $extRefId,
'status' => $topup_result_arr,
'network' => 'n.a'
];
$desc = [
'title' =>'customer top up by Merchant ID ' . $merchant->id,
'data' => $log_data,
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = $merchant->id;
$activity->user_type = 'merchant';
$activity->description = json_encode($desc);
$activity->save();
if ($topup_result_arr['code'] == 1) {
$merchant_update = Models\Merchant::find($merchant->id);
$merchant_update->balance = $prev_balance - $request->amount;
$merchant_update->save();
$desc = [
'title' =>'Successful customer top up by Merchant ID ' . $merchant->id,
'data' => ['previous_balance' => $merchant->balance, 'current_balance' => $merchant->balance - $request->amount],
'type' => 'merchant',
];
#update transactions
$transaction = Models\Transaction::find($transaction_id);
$transaction->status = $topup_result_arr['message'];
$transaction->save();
$activity = new ActivityLog;
$activity->user_id = $merchant->id;
$activity->user_type = 'merchant';
$activity->description = json_encode($desc);
$activity->save();
return response()->json([
'success' => true,
'message' => 'Top Up Successfully processed!'
], 200);
}
}
public function customerTopupStore(Request $request){
$request->validate([
'phone_number' => 'required|string',
'amount' => 'required|numeric|min:1',
'merchant_id' => 'required|integer|exists:merchants,id'
]);
$phone_number = preg_replace('/\s+/', '', $request->phone_number);
$check_phone = $this->validatePhoneNumber($phone_number);
if (!$check_phone) {
return response()->json(['success' => false, 'message' => 'Invalid phone number!'], 422);
}
$merchant = Models\Merchant::find($request->merchant_id);
$country_prefix = substr($check_phone, 0, 3);
$country = '';
switch ($country_prefix) {
case '265':
$country = 'MALAWI';
break;
case '260':
$country = 'ZAMBIA';
break;
default:
$country = '';
break;
}
if ($merchant->country !== $country) {
// find out from team
return response()->json(['success' => false, 'message' => 'Country not allowed!'], 400);
}
$extRefId = uniqid() . '-' . date('YmdHis');
$topup_result_arr = null;
try {
$topup_result = DB::transaction(function () use ($request, $check_phone, $extRefId, &$topup_result_arr) {
$merchant = Merchant::lockForUpdate()->find($request->merchant_id);
if ($merchant->balance < $request->amount) {
throw new \Exception('Insufficient balance!', 400);
}
$prev_balance = $merchant->balance;
$merchant->balance -= $request->amount;
$merchant->save();
$transaction = Transaction::create([
'msisdn' => $check_phone,
'amount' => $request->amount,
'merchant_id' => $merchant->id,
'session_id' => $extRefId,
'status' => 'PENDING'
]);
$click_params = [
"msisdn" => $check_phone,
"amount" => $request->amount,
"extRefId" => $extRefId
];
Log::info('ClickADP Request:', $click_params);
$api_response = $this->ClickADPHttp($click_params);
Log::info('ClickADP Response: ' . $api_response);
$this->sendNtfy($api_response);
$topup_result_arr = json_decode($api_response, true);
if (!$api_response || !isset($topup_result_arr['code'])) {
throw new \Exception('Top Up Could not be processed at this time. Try again later.', 502);
}
if ($topup_result_arr['code'] != 1) {
throw new \Exception($topup_result_arr['message'] ?? 'Provider failed to process topup.', 422);
}
$transaction->update(['status' => $topup_result_arr['message'] ?? 'SUCCESS']);
ActivityLog::create([
'user_id' => $merchant->id,
'user_type' => 'merchant',
'description' => json_encode([
'title' => 'Successful customer top up by Merchant ID ' . $merchant->id,
'data' => [
'msisdn' => $check_phone,
'amount' => $request->amount,
'previous_balance' => $prev_balance,
'current_balance' => $merchant->balance,
'session_id' => $extRefId,
'status' => $topup_result_arr,
'network' => 'n.a'
],
'type' => 'merchant',
])
]);
return true;
});
return response()->json([
'success' => true,
'message' => 'Top Up Successfully processed!'
], 200);
} catch (\Exception $e) {
Log::error('Topup Failed: ' . $e->getMessage());
try {
ActivityLog::create([
'user_id' => $request->merchant_id,
'user_type' => 'merchant',
'description' => json_encode([
'title' => 'Failed customer top up attempt',
'error' => $e->getMessage(),
'data' => ['msisdn' => $check_phone, 'amount' => $request->amount]
])
]);
// $activity = new ActivityLog;
// $activity->user_id = $merchant->id;
// $activity->user_type = 'merchant';
// $activity->description = json_encode($desc);
// $activity->save();
} catch (\Exception $logEx) {
Log::error('Failed to write failure activity log: ' . $logEx->getMessage());
}
$code = ($e->getCode() >= 400 && $e->getCode() <= 505) ? $e->getCode() : 500;
return response()->json([
'success' => false,
'message' => $e->getMessage()
], $code);
}
}
// Make sure to import your Models and Smsgateway at the top of the file
public function merchantToDealerRefund(Request $request){
$this->validate($request, [
'transaction_id' => 'required|string',
'reason' => 'required|string',
]);
$transaction = Models\MerchantTopUp::with('merchantInfo', 'orgInfo')->where('status', 'success')->where('transaction_id', $request->transaction_id)->first();
if ($transaction == null) {
return response()->json([
'success' => false,
'message' => 'Transaction not found.'
], 400);
}
$dealer = $transaction->orgInfo;
if ($transaction->merchantInfo->balance < $request->amount) {
//ask team to confirm if merchant can have a negative balance
return response()->json([
'success' => false,
'message' => 'Insufficient merchant balance to process this refund.'
], 400);
}
$merchant_name = $transaction->merchantInfo->fullname;
$dealer_name = $transaction->orgInfo->name;
$merchant = Models\Merchant::find($transaction->merchantInfo->id);
$organisation = Models\Organisation::find($transaction->orgInfo->id);
try {
//todo : remove one of organisation or dealer
DB::transaction(function () use ($request, $merchant, $organisation, $dealer, $transaction) {
Models\MerchantToDealerRefund::create([
'dealer_id' => $transaction->org_user_id,
'transaction_id'=> $request->transaction_id,
'merchant_id' => $transaction->merchant_id,
'amount' => $transaction->amount,
'dealer_msisdn' => $transaction->orgInfo->phone,
'reason' => $request->reason,
]);
$merchant->decrement('balance', $transaction->amount);
$organisation->increment('balance', $transaction->amount);
$desc = [
'title' => 'Dealer Refund',
'data' => "Refunded a dealer with an amount of " . $transaction->amount, // Typo fixed
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = $transaction->merchant_id;
$activity->user_type = 'merchant';
$activity->description = json_encode($desc);
$activity->save();
$transaction->status = 'refunded';
$transaction->save();
});
} catch (\Exception $e) {
\Log::error("API : Refund Transaction Failed: " . $e->getMessage());
return response()->json([
'success' => false,
'message' => "API XX Refund Transaction Failed: " . $e->getMessage()
], 400);
}
$msg = "You have successfully refunded a dealer ($dealer_name) with an amount of $transaction->amount.";
$dealer_msg = "You have received a refund amount of $transaction->amount from Airtime Merchant ($merchant_name).";
if (env('APP_ENV') !== 'local') {
try {
$smsgateway = new Smsgateway;
$smsgateway->sendSmsNew($merchant->phone, $msg);
$smsgateway->sendSmsNew($organisation->phone, $dealer_msg);
} catch (\Exception $e) {
\Log::error("API: Failed to send SMS for refund: " . $e->getMessage());
}
}
\Log::info("API :" . $msg);
\Log::info("API :" . $dealer_msg);
return response()->json([
'success' => true,
'message' => "Dealer Refund successfully processed! "
], 200);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginRequest;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthenticatedSessionController extends Controller
{
/**
* Display the login view.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('auth.login');
}
/**
* Handle an incoming authentication request.
*
* @param \App\Http\Requests\Auth\LoginRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(LoginRequest $request)
{
$request->authenticate();
$request->session()->regenerate();
return redirect()->intended(RouteServiceProvider::HOME);
}
/**
* Destroy an authenticated session.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Request $request)
{
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
class ConfirmablePasswordController extends Controller
{
/**
* Show the confirm password view.
*
* @return \Illuminate\View\View
*/
public function show()
{
return view('auth.confirm-password');
}
/**
* Confirm the user's password.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function store(Request $request)
{
if (! Auth::guard('web')->validate([
'email' => $request->user()->email,
'password' => $request->password,
])) {
throw ValidationException::withMessages([
'password' => __('auth.password'),
]);
}
$request->session()->put('auth.password_confirmed_at', time());
return redirect()->intended(RouteServiceProvider::HOME);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
class EmailVerificationNotificationController extends Controller
{
/**
* Send a new email verification notification.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(RouteServiceProvider::HOME);
}
$request->user()->sendEmailVerificationNotification();
return back()->with('status', 'verification-link-sent');
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
class EmailVerificationPromptController extends Controller
{
/**
* Display the email verification prompt.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function __invoke(Request $request)
{
return $request->user()->hasVerifiedEmail()
? redirect()->intended(RouteServiceProvider::HOME)
: view('auth.verify-email');
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;
class NewPasswordController extends Controller
{
/**
* Display the password reset view.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function create(Request $request)
{
return view('auth.reset-password', ['request' => $request]);
}
/**
* Handle an incoming new password request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'token' => ['required'],
'email' => ['required', 'email'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function ($user) use ($request) {
$user->forceFill([
'password' => Hash::make($request->password),
'remember_token' => Str::random(60),
])->save();
event(new PasswordReset($user));
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $status == Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
: back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
class PasswordResetLinkController extends Controller
{
/**
* Display the password reset link request view.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('auth.forgot-password');
}
/**
* Handle an incoming password reset link request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'email' => ['required', 'email'],
]);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$status = Password::sendResetLink(
$request->only('email')
);
return $status == Password::RESET_LINK_SENT
? back()->with('status', __($status))
: back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
class RegisteredUserController extends Controller
{
/**
* Display the registration view.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('auth.register');
}
/**
* Handle an incoming registration request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
return redirect(RouteServiceProvider::HOME);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*
* @param \Illuminate\Foundation\Auth\EmailVerificationRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function __invoke(EmailVerificationRequest $request)
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(RouteServiceProvider::HOME.'?verified=1');
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect()->intended(RouteServiceProvider::HOME.'?verified=1');
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AuthController extends Controller
{
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (!\Auth::attempt($request->only('email', 'password'))) {
return response()->json(['message' => 'Invalid credentials'], 401);
}
$user = \Auth::user();
// Create token
$token = $user->createToken('api-token')->plainTextToken;
return response()->json([
'user' => $user,
'token' => $token,
]);
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function log_query() {
// , $binding, $timing 'bindings' => $binding)
\DB::listen(function ($sql) {
\Log::info('Showing query', array('sql' => $sql));
//$encoded_sql = json_encode($sql);
//$this->sendNtfy("Showing Query : " . $encoded_sql);
}
);
}
public function validatePhoneNumber($phone){
//265998109217
//265881161942
//265881161942
//0881161942
$phone = str_replace(' ', '', $phone);
$phone = str_replace('+', '', $phone);
$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 $msisdn;
}
public function sendNtfy($data){
$url = 'https://ntfy.sh/airtimeSalesPortal';
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json'
),
CURLOPT_URL => $url,
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;
}
public function ClickADPHttp($params){
//$api_email, $api_token
$token = "54efd476-8d6e-483b-8b2a-c7904a8b4a6b";
$email = "srwb.airtime@clickairtime.com";
$X_Click_Airtime_Token = "54efd476-8d6e-483b-8b2a-c7904a8b4a6b";
$X_Click_Airtime_Email = "reseller@clickairtime.com";
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.clickairtime.com/adp",// "http://localhost:4646/api/request",
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 => json_encode($params),
CURLOPT_HTTPHEADER => array(
"X-Click-Airtime-Email: " . $X_Click_Airtime_Email,
"X-Click-Airtime-Token: " . $X_Click_Airtime_Token,
"Content-Type: application/json"
),
));
$response = curl_exec($curl);
\Log::info(curl_getinfo($curl));
curl_close($curl);
return $response;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\ActivityLog;
use App\Models\Topup;
use IlluminateAgnostic\Arr\Support\Carbon;
use App\Models;
use DB;
class DashboardController extends Controller
{
//
public function index(){
## DB Tables
/*
- merchant_top_ups - from dealers to merchants
- organisation_top_us - from Admin to dealers
- transactions - from merchants to end users
*/
$merchant = Models\Merchant::all()->count();
$dealers = Models\OrgUser::all()->count();
$activities = ActivityLog::with('admin')->orderBy('created_at', 'desc')->get()->take(10);
$dealer_topups = Models\OrganisationTopUp::with('orgInfo','userInfo')->orderBy('created_at','desc')->get();
$merchant_topups = Models\MerchantTopUp::with('merchantInfo','orgUserInfo')->orderBy('created_at','desc')->get();
// dd($merchant_topups);
$current_year_transactions = Models\Transaction::whereYear('created_at', date('Y'))->get();
$current_weeek_transactions = Models\Transaction::whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])->get();
$last_weeek_transactions = Models\Transaction::whereBetween('created_at', [now()->subWeek()->startOfWeek(), now()->subWeek()->endOfWeek()])->get();
$current_month_transactions = Models\Transaction::whereMonth('created_at', date('m'))->get();
$current_day_transactions = Models\Transaction::whereDate('created_at', date('Y-m-d'))->get();
#$top_merchants = Models\Transaction::select('merchant_id', 'amount')->with('merchant')->groupBy('amount', 'merchant_id')->orderByRaw('SUM(amount) DESC')->limit(4)->get();
$top_merchants = DB::table('transactions')
->select('transactions.merchant_id', 'merchants.fullname', DB::raw('SUM(amount) as total_amount'))
->join('merchants', 'transactions.merchant_id', '=', 'merchants.id')
->groupBy('fullname', 'merchant_id', 'amount')
->orderByRaw('SUM(amount) DESC')
->limit(4)
->get();
// dd($top_merchants);
// $top_merchants_by_sales = Models\Merchant::with('transactions')->orderBy('transactions', 'desc')->get();
// $top_dealers = Models\Organisation::with('transactions')->orderBy('transactions', 'desc')->get();
$data = [
'merchant_count' => $merchant,
'dealer_count' => $dealers,
'activities' => $activities,
'dealer_topup_count' =>$dealer_topups->count(),
'dealer_topup_sum' =>$dealer_topups->sum('amount'),
'dealer_topups' => $dealer_topups->take(5),
'merchant_topups' => $merchant_topups->take(10),
'merchant_topup_count' => $merchant_topups->count(),
'merchant_topup_sum' => $merchant_topups->sum('amount'),
'current_weeek_transactions' => $current_weeek_transactions->sum('amount'),
'current_month_transactions' => $current_month_transactions->sum('amount'),
'last_weeek_transactions' => $last_weeek_transactions->sum('amount'),
'current_day_transactions' => $current_day_transactions->sum('amount'),
'top_merchants' => $top_merchants->take(5),
// 'top_dealers' => $top_dealers->take(5),
'transactions' => $this->transactions(),
'current_year_transactions_sum' => $current_year_transactions->sum('amount'),
'page_title' => 'Dashboard'
];
// dd($data['top_merchants']);
return view('dashboard.index', $data);
}
public function transactions(){
$transactions = \DB::table('transactions')
->select(\DB::raw('SUM(amount) as total_sales, MONTH(created_at) as theMonth'))
->groupByRaw('month(created_at)')
->get();
/*
$transactions = Transaction::select('id', 'created_at')->get()
->groupBy(function($date) {
//return Carbon::parse($date->created_at)->format('Y'); // grouping by years
return Carbon::parse($date->created_at)->format('m'); // grouping by months
});
dd($transactions);
$transactionmcount = [];
$transactionArr = [];
foreach ($transactions as $key => $value) {
$transactionmcount[(int)$key] = count($value);
}
for($i = 1; $i <= 12; $i++){
if(!empty($transactionmcount[$i])){
$transactionArr[$i] = $transactionmcount[$i];
}else{
$transactionArr[$i] = 0;
}
}
$transactions = '';
foreach ($transactionArr as $tran) {
$transactions = $transactions.','.$tran;
}
*/
return $transactions; //response()->json($transactions);
}
public function merchant(){
$merchant_session = session('merchant');
$merchant = Models\Merchant::find($merchant_session->id);
$merchant_trans = Models\Transaction::where('merchant_id',$merchant->id)->orderBy('id', 'DESC')->get();
$data = [
'transaction_count' => $merchant_trans->count(),
'transactions' => $merchant_trans->take(5),
'current_balance' => $merchant['balance'],
'transactions_graph' => $this->merchant_transactions($merchant),
];
// dd($data);
return view('dashboard.merchant',$data);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ExcelProcessingController extends Controller
{
public function index(){
dd("foo bar");
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class LandingPageController extends Controller
{
public function index(){
$data = [
'page_title' => 'Airtime Sales Portal',
];
// dd($data);
return view('landing.index', $data);
}
}

View File

@@ -0,0 +1,327 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Validator;
use Illuminate\Support\Facades\Auth;
use App\User;
use App\Models;
use Carbon\Carbon;
use App\ActivityLog;
use Illuminate\Support\Facades\Mail;
use App\Mail\ResetPassword;
use App\Models\Merchant;
use Session;
use Illuminate\Validation\Rule;
class LoginController extends Controller
{
//Login page
public function login_page(Request $request){
// dd($request->headers->get('referer'));
// session(['previous_page' => $request->headers->get('referer')]);
// dd(session());
return view('login.admin.index');
}
//Login user
public function login_user(Request $request){
$this->validate($request, ['email' => 'required', 'password' => 'required']);
$email = $request->input('email');
$password = $request->input('password');
$login = auth()->attempt(['email' => $email, 'password' => $password]);
if ($login) {
$user = auth()->user();
Auth::login($user, true);
if (Auth::user()->deleted_at !== null) {
return redirect()->back()->with('login-error', 'Account Suspended!');
}
$desc = [
'title'=>'Login',
'data'=>$user->name,
'type'=>'login',
];
$activity = new ActivityLog;
$activity->user_id = Auth::user()->id;
$activity->user_type = 'admin';
$activity->description = json_encode($desc);
$activity->save();
return redirect(url('admin'));
}
else {
return redirect()->back()->with('login-error', 'Email/Password incorrect!');
}
}
//Logout User
public function logout(){
auth()->logout();
return redirect()->to('/login');
}
//Add user
public function add_user(){
$password = bcrypt('kweku20');
$user = new User;
$user->name = 'Kweku Debrah';
// $user->lastname = 'Debrah';
$user->email='kweku.debrah.emma@gmail.com';
$user->password = $password;
$save = $user->save();
}
public function show_merchant_register(){
$data = [
'page_title' => 'Merchant Registration',
];
return view('login.merchant.register', $data);
}
public function merchant_register(Request $request){
// $this->validate($request, [
// 'fullname' => 'required',
// 'phone'=> 'required|unique:merchants,phone',
// 'language' => 'required'
// ]);
$request->validate([
'fullname' => 'required',
'phone'=> 'required|unique:merchants,phone',
// 'language' => 'required',
'country_iso' => 'required', Rule::in(['MW', 'ZM'])
], [
'country_iso.exists' => 'Sorry, we do not currently support accounts from this country.',
]);
$merchant_arr = $request->except('_token');
/*
$phone = $this->validatePhoneNumber($request->phone);
if ($phone == false) {
Session::flash('error_message', 'Invalid phone number format. Check and try again');
return redirect()->back()->withInput();
}
*/
//todo : make the currency and country dynamic
$country = ($request->country_iso == 'MW') ? "MALAWI" : 'ZAMBIA';
$currency = ($request->country_iso == 'MW') ? "MWK" : 'ZMW';
$phone = str_replace('+', '', $request->phone);
$merchant_arr['phone'] = $phone;
$merchant_arr['language'] = 'english';
$merchant_arr['country'] = $country;
$merchant_arr['currency'] = $currency;
$result = Models\Merchant::create($merchant_arr);
if ($result == null) {
Session::flash('error_message', 'Your request could not be handled at this time. Try again later');
return redirect()->back()->withInput();
}
return redirect('merchant/register-success');
}
public function register_success(){
$data = ['page_title' => 'Merchant Sign Successful'];
return view('login.merchant.register-success',$data);
}
public function register_page(Request $request){
// $email = $request->input('email');
// $user = User::where('email', $email)->first();
// if ($user== null) {
// abort(404);
// }
$data = [
// 'user'=>$user,
];
return view('login.register', $data);
}
public function reset_password(Request $request){
$validator = Validator::make($request->all(), [
'user' => 'required',
'password'=>'required',
'cpassword'=>'required',
]);
if($validator->fails()){
return redirect()->back()->with('login-error', 'Enter required data!');
}
if ($request->input('password') !== $request->input('cpassword')) {
return redirect()->back()->with('login-error', 'Password dont Match!');
}
$user = $request->input('user');
$password = $request->input('password');
$user = User::where('id',$user)->update([
'password'=>bcrypt($password),
]);
return redirect()->to('/login')->with('login-success', 'Password Changed Successfully, Login to continue');
}
public function forgot_page(){
return view('login.forgot');
}
public function sendResetMail(Request $request){
// dd($request->all());
$validator = Validator::make($request->all(), [
'email' => 'required',
]);
if($validator->fails()){
return redirect()->back()->with('login-error', 'Enter required data!');
}
$email = $request->input('email');
$user = User::where('email', $email)->first();
if ($user == null ) {
return redirect()->back()->with('login-error', 'Email Doesnt Exist!');
}
$newUser = [
'name' => $user->name,
'email' => $email,
];
Mail::to($email)->send(new ResetPassword($newUser));
return redirect()->to('/confirm-mail');
}
public function confirmMail()
{
return view('login.confirm');
}
/**
* Merchant Login Section
*/
public function merchant_login_page(){
return view('login.merchant.index');
}
public function merchant_login(Request $request){
$request->validate([
'phone_number' => 'required|string',
'pin' => 'required|string',
'country_iso' => 'required', Rule::in(['MW', 'ZM'])
], [
'country_iso.exists' => 'Sorry, we do not currently support accounts from this country.',
]);
// if($validator->fails()){
// return redirect()->back()->with('login-error', 'Phone Number/PIN is required!');
// }
/*
$phone_number = $this->validatePhoneNumber($request->phone_number);
if($phone_number == false){
return redirect()->back()->with('login-error', 'Phone Number/PIN is incorrect!');
}
*/
// $phone_number = $request->input('phone_number'); | use hashing
$pin = md5($request->input('pin'));
// dump($request->all());
$phone_number = str_replace('+', '', $request->phone_number);
$merchant = Merchant::where('phone', $phone_number)->where('pin', $pin)->first();
// dd($merchant);
if($merchant == null){
return redirect()->back()->with('login-error', 'Phone Number/PIN incorrect!');
}
session(['merchant' => $merchant]);
return redirect()->to('merchant/landing');
}
public function merchant_logout(){
session(['merchant' => null]);
// return redirect()->to('/merchant/login');
return redirect()->to('/');
}
public function checkSupportedCountry(Request $request){
$request->validate([
'iso_code' => 'required|string|size:2'
]);
$isoCode = strtoupper($request->iso_code);
//todo retrieve this from the DB later
// $isSupported = DB::table('supported_countries')->where('iso_code', $isoCode)->exists();
$supported_arr = ['MW', 'ZM'];
if (in_array($isoCode, $supported_arr)) {
return response()->json(['supported' => true]);
} else {
return response()->json([
'supported' => false,
'message' => 'Sorry, we currently do not support accounts from this country.'
]);
}
}
}

View File

@@ -0,0 +1,785 @@
<?php
//this is a comment
namespace App\Http\Controllers;
use App\Models\Merchant;
use App\Models\Organisation;
use App\Models\MerchantToDealerRefund;
use App\User;
use App\Models;
use PragmaRX\Countries\Package\Countries;
use Illuminate\Http\Request;
use Carbon\Carbon;
use App\ActivityLog;
use Validator;
use Illuminate\Support\Facades\Auth;
use App\Models\Topup;
use App\Models\Transaction;
use App\Libs\Smsgateway;
use Session;
use App\Rules\PhoneValidationRule;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class MerchantController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
dd('index');
$merchants = Models\Merchant::latest()->paginate(10);
$data = [
'merchants'=>$merchants,
'merchants_list' => Models\Merchant::get(),
'page_title' => 'merchant_list'
];
return view('organisation.merchants',$data);
}
public function landing(){
$merchant_session = session('merchant');
$merchant = Models\Merchant::find($merchant_session->id);
// dd($merchant);
$merchant_trans = Models\Transaction::where('merchant_id', $merchant->id)->orderBy('id', 'DESC')->get();
$data = [
'transaction_count' => $merchant_trans->count(),
'transaction_sum' => $merchant_trans->sum('amount'),
'transactions' => $merchant_trans->take(15),
'current_balance' => $merchant['balance'],
'transactions_graph' => $this->merchant_transactions($merchant),
'merchant' => $merchant,
'page_title' => 'Merchant Dashboard',
];
// dd($data['transactions_graph']);
return view('merchants.landing',$data);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create(){
$data = [
'page_title' => 'Merchant Registration',
];
return view('organisation.create_merchant', $data);
}
public function showrefund(){
$merchant = session('merchant');
$dealers = Models\Organisation::get();
$recent_refunds = Models\MerchantToDealerRefund::with('orgInfo')->orderBy('id', 'DESC')->paginate(15);
$data = [
'page_title' => 'Merchant Refund',
'dealers_arr' => $dealers,
'recent_refunds' => $recent_refunds,
'merchant' => $merchant
];
// dd($data);
return view('merchants.refund', $data);
}
public function showchangepin(){
$merchant = session('merchant');
$data = [
'page_title' => 'Merchant Refund',
'merchant' => $merchant
];
return view('merchants.showchangepin', $data);
}
public function refundStore(Request $request){
$this->validate($request, [
// 'dealer_id' => 'required|integer',
// 'merchant_id' => 'required|integer',
// 'amount' => 'required|numeric|min:0.01',
'transaction_id' => 'required|string',
'reason' => 'required|string',
]);
/*
$merchant = Models\Merchant::find($request->merchant_id);
if ($merchant == null) {
return redirect()->to('merchant/refunds')->with('error_message', 'Merchant not found');
}
*/
/*
$organisation = Models\Organisation::find($request->dealer_id);
if ($organisation == null) {
return redirect()->to('merchant/refunds')->with('error_message', 'Dealer not found');
}
*/
$transaction = Models\MerchantTopUp::with('merchantInfo', 'orgInfo')->where('status', 'success')->where('transaction_id', $request->transaction_id)->first();
if ($transaction == null) {
return response()->json([
'success' => false,
'message' => 'Transaction not found.'
], 400);
}
$dealer = $transaction->orgInfo; #Models\OrgUser::findOrFail($request->dealer_id);
if ($transaction->merchantInfo->balance < $request->amount) {
//ask team to confirm if merchant can have a negative balance
return response()->json([
'success' => false,
'message' => 'Insufficient merchant balance to process this refund.'
], 400);
}
$merchant_name = $transaction->merchantInfo->fullname;
$dealer_name = $transaction->orgInfo->name;
$merchant = Models\Merchant::find($transaction->merchantInfo->id);
$organisation = Models\Organisation::find($transaction->orgInfo->id);
try {
//todo : remove one of organisation or dealer
DB::transaction(function () use ($request, $merchant, $organisation, $dealer, $transaction) {
Models\MerchantToDealerRefund::create([
'dealer_id' => $organisation->org_id,
'merchant_id' => $transaction->merchant_id,
'amount' => $transaction->amount,
'dealer_msisdn' => $transaction->orgInfo->phone,
'reason' => $request->reason,
]);
$merchant->decrement('balance', $transaction->amount);
$organisation->increment('balance', $transaction->amount);
$desc = [
'title' => 'Dealer Refund',
'data' => "Refunded a dealer with an amount of " . $transaction->amount, // Typo fixed
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = $request->session()->get('current_merchant.id');
$activity->user_type = 'merchant';
$activity->description = json_encode($desc);
$activity->save();
$transaction->status = 'refunded';
$transaction->save();
});
} catch (\Exception $e) {
\Log::error("Refund Transaction Failed: " . $e->getMessage());
return response()->json([
'success' => false,
'message' => "Refund Transaction Failed: " . $e->getMessage()
], 400);
}
$msg = "You have successfully refunded a dealer ($dealer_name) with an amount of $transaction->amount.";
$dealer_msg = "You have received a refund amount of $transaction->amount from Airtime Merchant ($merchant_name).";
if (env('APP_ENV') !== 'local') {
try {
$smsgateway = new Smsgateway;
$smsgateway->sendSmsNew($merchant->phone, $msg);
$smsgateway->sendSmsNew($organisation->phone, $dealer_msg);
} catch (\Exception $e) {
\Log::error("Failed to send SMS for refund: " . $e->getMessage());
}
}
\Log::info($msg);
\Log::info($dealer_msg);
return response()->json([
'success' => false,
'message' => "Dealer Refund successfully processed! "
], 200);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request){
$validatedData = $request->validate([
'fullname' => 'required',
// 'phone_number'=> 'required|unique:merchants,phone',
'phone_number' => ['required', 'unique:merchants,phone', new PhoneValidationRule],
'language' => 'required'
]);
/*
$check_phone = $this->validatePhoneNumber($request->phone_number);
if ($check_phone == false) {
if($request->ajax()){
return response()->json([
'code' => 3,
'status' => 'fail',
'message' => 'Invalid phone number'
]);
}
else{
return redirect()->back()->withInput()->with('error_message', 'Invalid phone number!');
}
}
*/
$fullname = $request->input('fullname');
$phone_number = $request->input('phone_number');
$pin = rand(1000, 9999);
$merchant = Models\Merchant::create([
'fullname' => $fullname,
'phone' => $request->phone_number, // $check_phone,
'pin' => md5($pin),
'org_id' => session('current_org_user.org_id'),
'language' => 'english'
]);
$desc = [
'title' => 'Created a merchant',
'data' => $fullname,
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = $request->session()->get('current_org_user.id'); // org_user_id ;
$activity->user_type = 'organisation';
$activity->description = json_encode($desc);
$activity->save();
// TODO: send sms with PIN to merchant phone -
$msg = "You have been Successfully registered as an Airtime sales merchant. Your 4-digit PIN is $pin. You will need it for all your activities. Dial *349# to begin.";
$smsgateway = new Smsgateway;
if(env('APP_ENV') !== 'local'){
$result = $smsgateway->sendSmsNew($check_phone, $msg);
}
\Log::info($msg);
$this->sendNtfy($msg);
if($request->ajax()){
return response()->json([
'code' => 1,
'status' => 'success',
'message' => 'Merchant Created Successfully'
]);
}
else{
return redirect()->to('organisation/merchants')->with('success_message', 'Successfully Added New Merchant');
}
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
// dd($id);
// $merchant = Merchant::where('id',$id)->with('transactions')->get()[0];
$merchant = Merchant::with('transactions')->find($id);
$merchants = Models\Merchant::get();
$merchant_topups = Models\MerchantTopUp::where('merchant_id', $id)->latest()->paginate(20);
$data = [
'merchant' => $merchant,
'merchants_list' => $merchants,
'page_title' => 'Merchant Details | ' . $merchant->fullname,
'merchant_topups' => $merchant_topups,
'merchant_id' => $id,
];
return view('organisation.merchant_show',$data);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
$merchant = Models\Merchant::findOrfail($id);
// $countries = Countries::all();
$data = [
'merchant' => $merchant,
'page_title' => 'Merchant Edit',
];
// dd($data);
return view('organisation.merchant_edit', $data);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->validate($request, [
'fullname' => 'required',
'phone'=> 'required',
'status' => 'required',
'language' => 'required'
]);
$check_phone = $this->validatePhoneNumber($request->phone);
if ($check_phone == false) {
return redirect()->back()->withInput()->with('admin-error', 'Invalid phone number!');
}
// dd($request->all());
Merchant::where('id', $id)->update([
'fullname' => $request->fullname,
'phone' => $check_phone,
'language' => $request->language,
'status' => $request->status
]);
$desc = [
'title' =>'Updated merchant',
'data' => $request->fullname,
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = session('current_org_user.id');// organisation user ID
$activity->user_type = 'organisation';
$activity->description = json_encode($desc);
$activity->save();
return redirect()->to('organisation/merchants')->with('admin-success', 'Successfully Updated Merchant details');
}
public function changePin(Request $request)
{
// dd($request->ajax());
$this->validate($request, [
'current_pin' => 'required',
'new_pin'=> 'required',
'merchant_id' => 'required'
]);
// verify current PIN | 81dc9bdb52d04dc20036dbd8313ed055
$current_pin = trim($request->current_pin);
$merchant = Merchant::find($request->merchant_id);
if(md5($current_pin) !== $merchant->pin){
return response()->json([
'code' => 3,
'status' => 'failed',
'message' => 'Current PIN is incorrect, check and try again!'
]);
}
$new_pin = md5($request->new_pin);
Merchant::where('id', $request->merchant_id)->update(['pin' => $new_pin]);
$desc = [
'title' =>'Merchant PIN change',
'data' => "PIN successfully changed for merchant ID: " . $request->merchant_id,
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = session('current_org_user.id');// organisation user ID
$activity->user_type = 'organisation';
$activity->description = json_encode($desc);
$activity->save();
return response()->json([
'code' => 1,
'status' => 'success',
'message' => 'PIN Successfully Changed!'
]);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
Models\Merchant::where('id', $id)->update([
'status' => 'disabled',
]);
$activity = new ActivityLog;
$activity->user_id = session('current_org_user.id'); // organisation user ID
$activity->user_type = 'organisation';
$activity->description = 'disabled a merchant with ID : ' . $id ;
$activity->save();
return redirect()->to('organisation/merchant')->with('admin-sucsess', 'Successfully Disabled Merchant');
}
public function enable($id)
{
Models\Merchant::where('id', $id)->update([
'status'=>'active',
]);
$activity = new ActivityLog;
$activity->user_id = session('current_org_user.id');
$activity->user_type = 'organisation';
$activity->description = 'enable a merchant with ID : ' . $id ;
$activity->save();
return redirect()->to('/organisation/merchant')->with('admin-sucsess', 'Successfully Enabled Merchant');
}
public function addTopUp(Request $request){
// dd($request->all());
$validator = Validator::make($request->all(), [
'amount' => 'required',
'payment'=>'required',
'merchant'=>'required',
]);
if($validator->fails()){
return redirect()->back()->with('admin-error', 'Enter required data!');
}
$amount = $request->input('amount');
$payment = $request->input('payment');
$merchant = $request->input('merchant');
$topup = new TopUp;
$topup->amount = $amount;
$topup->merchant_id = $merchant;
$topup->created_by = $request->session()->get('current_org_user.id');
$topup->payment_method = $payment;
$save = $topup->save();
//Update Merchant Amt //
Models\Merchant::where('id', $merchant)->increment('balance', $amount);
if ($save) {
$merchant = Models\Merchant::find($merchant);
$desc = [
'title'=>'Added new Top Up',
'data'=>'K ' .$topup->amount.' for '.$merchant->fullname.' payment by '.$payment,
'type'=>'topup',
];
$activity = new ActivityLog;
$activity->user_id = $request->session()->get('current_org_user.id');
$activity->user_type = 'organisation';
$activity->description = json_encode($desc);
$activity->save();
return redirect()->to('/organisation/merchant/'.$merchant->id)->with('admin-sucsess', 'Successfully Added Top Up');
} else {
return redirect()->back()->with('admin-error', 'Error');
}
}
public function transactions(){
$merchant = session('merchant');
// $transactions = Transaction::where('merchant_id', $merchant->id)->latest()->paginate(20);
$data = [
// 'transactions'=>$transactions,
'merchant' => $merchant,
'page_title' => 'Merchant Transactions',
];
return view('merchants.transactions',$data);
}
public function getTransactionsJson(Request $request){
$merchant_id = session('merchant.id');
$transactions_arr = \DB::table('transactions')
->join('merchants', 'transactions.merchant_id', '=', 'merchants.id')
->select('merchants.fullname', 'transactions.id', 'transactions.status', 'transactions.msisdn', 'transactions.amount', 'transactions.created_at')
->whereRaw('transactions.merchant_id = '. $merchant_id)
->orderBy('transactions.created_at', 'DESC')
->paginate(20);
if($request->has('keyword')){
$queries = [];
$keyword = $request->keyword;
$queries['keyword'] = $keyword;
$transactions_arr = \DB::table('transactions')
// ->join('org_users', 'transactions.org_user_id', '=', 'org_users.id')
->join('merchants', 'transactions.merchant_id', '=', 'merchants.id')
->select('merchants.fullname', 'transactions.id', 'transactions.status', 'transactions.msisdn', 'transactions.amount', 'transactions.created_at')
->whereRaw("merchants.fullname LIKE '%$keyword%' OR transactions.msisdn LIKE '%$keyword%' OR transactions.status LIKE '%$keyword%' OR transactions.amount LIKE '%$keyword%' OR transactions.created_at LIKE '%$keyword%'")
->whereRaw('transactions.merchant_id = '. $merchant_id)
->orderBy('transactions.created_at', 'DESC')
->paginate(20)->appends($queries);
}
return response()->json($transactions_arr);
}
public function dealer_transactions(){
$merchant = session('merchant');
$data = [
'merchant' => $merchant,
'page_title' => 'Dealer Transactions',
];
// dd($data);
return view('merchants.dealer-transactions',$data);
}
public function getDealerListJson(Request $request){
$merchant_id = session('merchant.id');
$dealer_arr = \DB::table('organisations')->orderBy('organisations.created_at', 'DESC')->paginate(20);
if($request->has('keyword')){
$queries = [];
$keyword = $request->keyword;
$queries['keyword'] = $keyword;
$dealer_arr = \DB::table('organisations')
->whereRaw("name LIKE '%$keyword%' OR phone LIKE '%$keyword%' OR status LIKE '%$keyword%' OR email LIKE '%$keyword%' OR created_at LIKE '%$keyword%'")
->orderBy('created_at', 'DESC')
->paginate(20)->appends($queries);
}
return response()->json($dealer_arr);
}
public function get_dealers(){
$merchant = session('merchant');
$data = [
'merchant' => $merchant,
'page_title' => 'Dealers',
];
return view('merchants.dealer-list', $data);
}
public function getDealerTransactionsJson(Request $request){
$merchantId = session('merchant.id');
$query = \DB::table('merchant_top_ups')
->join('merchants', 'merchant_top_ups.merchant_id', '=', 'merchants.id')
->join('org_users', 'merchant_top_ups.org_user_id', '=', 'org_users.id')
->select([
'merchants.fullname',
'org_users.phone',
'org_users.name',
// 'merchant_top_ups.id',
'merchant_top_ups.transaction_id',
'merchant_top_ups.status',
'merchant_top_ups.amount',
'merchant_top_ups.created_at',
])
->where('merchant_top_ups.merchant_id', $merchantId)
->orderByDesc('merchant_top_ups.created_at');
if ($request->filled('keyword')) {
$keyword = $request->input('keyword');
$like = '%' . $keyword . '%';
$query->where(function ($q) use ($like, $keyword) {
$q->where('org_users.phone', 'like', $like)
->orWhere('org_users.name', 'like', $like)
->orWhere('merchants.fullname', 'like', $like)
->orWhere('merchant_top_ups.status', 'like', $like)
->orWhere('merchant_top_ups.amount', 'like', $like)
->orWhere('merchant_top_ups.created_at', 'like', $like);
});
}
$transactions = $query->paginate(20);
if ($request->filled('keyword')) {
$transactions->appends(['keyword' => $request->input('keyword')]);
}
return response()->json($transactions);
}
public function showCustomerTopForm(){
//$merchant = Merchant::where('id',$id)->with('transactions')->get()[0];
$data = [
'page_title' => 'Customer Top Up',
'page_title' => 'merchant_topups'
];
return view('merchant.customer-topup',$data);
}
public function customerTopupStore(Request $request){
$this->validate($request, [
'phone_number'=> 'required|numeric',
'amount' => 'required|numeric|min:1'
]);
$phone_number = preg_replace('/[+\s]+/', '', $request->phone_number); // preg_replace('/\s+/', '', $request->phone_number);
/*
$check_phone = $this->validatePhoneNumber($phone_number);
if ($check_phone == false) {
return redirect()->back()->withInput()->with('admin-error', 'Invalid phone number!');
}
*/
$merchant_session = session('merchant');
$merchant = Models\Merchant::find($merchant_session['id']);
// dd($merchant);
$country_prefix = substr($phone_number, 0, 3);
// dd($country_prefix);
$country = '';
switch ($country_prefix) {
case '265':
$country = 'MALAWI';
break;
case '260':
$country = 'ZAMBIA';
break;
default:
$country = '';
break;
}
if ($merchant->country !== $country) {
// find out from team
return redirect()->back()->withInput()->with('admin-error', 'Country not allowed!');
}
$difference = $merchant->balance - $request->amount;
if ($merchant->balance < $request->amount) {
return redirect()->back()->withInput()->with('admin-error', 'Insufficient balance!');
}
$prev_balance = $merchant->balance;
//get merchant ID, amount, msisdn,
$extRefId = uniqid() . '-' . date('YmdHis');
#$last_insert_id = saveTransaction($session_data['top_up_phone'], $session_data['top_up_amount'], $session_data['merchant_id'], $extRefId);
$transaction_arr = [
'msisdn' => $phone_number,
'amount' => $request->amount,
'merchant_id' => $merchant->id,
'session_id' => $extRefId
];
// dd($transaction_arr);
$trans_result = Models\Transaction::create($transaction_arr);
$transaction_id = $trans_result->id;
$click_params = [
"msisdn" => $phone_number,
"amount" => $request->amount,
"extRefId" => $extRefId
];
\Log::info($click_params);
//$merchant->orgInfo->api_email, $merchant->orgInfo->api_token
$topup_result = $this->ClickADPHttp($click_params); // Click ADP
\Log::info($topup_result);
$this->sendNtfy($topup_result);
$topup_result_arr = json_decode($topup_result, TRUE);
\Log::info($topup_result_arr);
if ($topup_result == false) {
return redirect()->back()->withInput()->with('admin-error', 'Top Up Could not be processed at this time. Try again later!');
}
$log_data = [
'msisdn' => $phone_number,
'amount' => $request->tamount,
'user_id' => $merchant->id,
'merchant_balance' => $merchant->balance,
'session_id' => $extRefId,
'status' => $topup_result_arr,
'network' => 'n.a'
];
$desc = [
'title' =>'customer top up by Merchant ID ' . $merchant->id,
'data' => $log_data,
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = $merchant->id;
$activity->user_type = 'merchant';
$activity->description = json_encode($desc);
$activity->save();
if ($topup_result_arr['code'] == 1) {
$merchant_update = Models\Merchant::find($merchant->id);
$merchant_update->balance = $prev_balance - $request->amount;
$merchant_update->save();
$desc = [
'title' =>'Successful customer top up by Merchant ID ' . $merchant->id,
'data' => ['previous_balance' => $merchant->balance, 'current_balance' => $merchant->balance - $request->amount],
'type' => 'merchant',
];
#update transactions
$transaction = Models\Transaction::find($transaction_id);
$transaction->status = $topup_result_arr['message'];
$transaction->save();
$activity = new ActivityLog;
$activity->user_id = $merchant->id;
$activity->user_type = 'merchant';
$activity->description = json_encode($desc);
$activity->save();
Session::flash('success_message', 'Top Up Successfully processed!');
return redirect('merchant');
}
return redirect('merchant/transactions');
}
public function merchant_transactions($merchant){
$transactions = Models\Transaction::where('merchant_id', $merchant->id)->select('id', 'created_at')->get()
->groupBy(function($date) {
//return Carbon::parse($date->created_at)->format('Y'); // grouping by years
return Carbon::parse($date->created_at)->format('m'); // grouping by months
});
$transactionmcount = [];
$transactionArr = [];
return response()->json(['transactions' => $transactions]);
$pp = 1;
$trans = [];
foreach ($transactions as $value) {
if ($pp == $value) {
$trans[$pp]= $value;
}
else{
$trans[$pp] = 0;
}
}
foreach ($transactions as $key => $value) {
$transactionmcount[(int)$key] = count($value);
}
for($i = 1; $i <= 12; $i++){
if(!empty($transactionmcount[$i])){
$transactionArr[$i] = $transactionmcount[$i];
}else{
$transactionArr[$i] = 0;
}
}
$transactions = '';
foreach ($transactionArr as $tran) {
$transactions = $transactions.','.$tran;
}
return $transactions;
}
public function merchant_transactions_graph($merchant_id = 1){
$transactions = Models\Transaction::where('merchant_id', $merchant_id)->pluck('amount', 'created_at');
// return response()->json($transactions);
// dump($transactions);
return $transactions;
}
public function getActivity()
{
//
$merchant = session('merchant');
$activities = ActivityLog::with('admin')->where('user_type', 'merchant')->where('user_id', $merchant->id)
->orderBy('created_at', 'desc')->paginate(30);
$data = [
'activities' => $activities,
'merchant' => $merchant,
'page_title' => 'Merchant Activity Log'
];
return view('merchants.activity', $data);
}
}

View File

@@ -0,0 +1,202 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Carbon\Carbon;
use App\ActivityLog;
use Illuminate\Support\Facades\Mail;
use App\Mail\ResetPassword;
use App\Models\Merchant;
use Validator;
use App\Models;
use Session;
use Illuminate\Support\Facades\Hash;
class OrganisationLoginController extends Controller
{
public function login_page(Request $request){
session(['pre_page' => $request->headers->get('referer')]);
return view('login.organisation.index');
}
//Add user
public function add_user(){
dd('foo bar');
// $password = bcrypt('kweku20');
// $user = new User;
// $user->name = 'Kweku Debrah';
// // $user->lastname = 'Debrah';
// $user->email='kweku.debrah.emma@gmail.com';
// $user->password = $password;
$save = $user->save();
}
public function register_page(){
$data = ['page_title' => 'Dealer Sign Up'];
return view('login.organisation.register',$data);
}
public function register_success(){
$data = ['page_title' => 'Dealer Sign Successful'];
return view('login.organisation.register-success',$data);
}
public function register(Request $request){
$this->validate($request, [
'name' => 'required',
'phone'=> 'required|unique:organisations,phone',
'email' => 'required'
]);
$org_arr = $request->except('_token');
$phone = $this->validatePhoneNumber($request->phone);
// dd($phone);
if ($phone == false) {
Session::flash('error_message', 'Invalid phone number format. Check and try again');
return redirect()->back()->withInput();
}
$org_arr['phone'] = $phone;
$result = Models\Organisation::create($org_arr);
if ($result == null) {
Session::flash('error_message', 'Your request could not be handled at this time. Try again later');
return redirect()->back()->withInput();
}
return redirect('dealer/register-success');
}
public function resetpassword(Request $request){
$this->validate($request, [
'user_id' => 'required',
'current_password' => 'required',
'new_password' => 'required',
'confirm_password' => 'required'
]);
// if($validator->fails()){
// return redirect()->back()->with('login-error', 'Enter required data!');
// }
$org_user = Models\OrgUser::find($request->user_id);
if (!Hash::check($request->current_password, $org_user->password)) {
return redirect()->back()->with('login-error', 'Incorrect current password!');
}
if ($request->input('new_password') !== $request->input('confirm_password')) {
return redirect()->back()->with('login-error', 'New Passwords do not match!');
}
$new_password = Hash::make($request->new_password);
$user = Models\OrgUser::where('id', $request->user_id)->update([
'password' => $new_password,
]);
return redirect()->to('organisation/reset-password')->with('login-success', 'Password Changed Successfully, Login to continue');
}
public function forgot_page(){
return view('login.forgot');
}
public function resetCreate(){
$user = session('current_org_user');
$data = [
'user' => $user,
'page_title' => 'Password Reset'
];
return view('organisation.reset', $data);
}
public function sendResetMail(Request $request){
// dd($request->all());
$validator = Validator::make($request->all(), [
'email' => 'required',
]);
if($validator->fails()){
return redirect()->back()->with('login-error', 'Enter required data!');
}
$email = $request->input('email');
$user = User::where('email', $email)->first();
if ($user == null ) {
return redirect()->back()->with('login-error', 'Email Doesnt Exist!');
}
$newUser = [
'name' => $user->name,
'email' => $email,
];
Mail::to($email)->send(new ResetPassword($newUser));
return redirect()->to('/confirm-mail');
}
public function confirmMail()
{
return view('login.confirm');
}
public function handleLogin(Request $request){
// $this->log_query();
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'password' =>'required'
]);
if($validator->fails()){
return redirect()->back()->with('login-error', 'Email/Password required!');
}
$org_user = Models\OrgUser::with('org_info')->where('email', $request->email)->first();
// dd($org_user);
if($org_user == null){
return redirect()->back()->with('login-error', 'Email/Password Incorrect!');
}
if (Hash::check($request->password, $org_user->password)) {
$request->session()->regenerate(true);
$request->session()->put('current_org_user.id', $org_user->id);
$request->session()->put('current_org_user.name', $org_user->name);
$request->session()->put('current_org_user.email', $org_user->email);
$request->session()->put('current_org_user.org_id', $org_user->id); //remove this in the next update
$request->session()->put('current_org_user.org_name', $org_user->org_info->name);
$request->session()->put('current_org_user.currency', $org_user->org_info->currency);
$desc = [
'title' => 'Login',
'data' => $org_user->name . ' Successful login'
];
$activity = new ActivityLog;
$activity->user_id = $request->session()->get('current_org_user.id');
$activity->user_type = 'organisation';
$activity->description = json_encode($desc);
$activity->save();
return redirect()->to('organisation');
}
else{
return redirect()->back()->with('login-error', 'Email or Password incorrect!');
}
}
public function logout(){
session(['current_org_user' => null]);
// return redirect()->to('/organisation/login');
return redirect()->to('/');
}
}

View File

@@ -0,0 +1,334 @@
<?php
//dealers are not using the OrgUsers like SRWB
namespace App\Http\Controllers;
use App\Models;
use Illuminate\Http\Request;
use App\Libs\Smsgateway;
use Session;
use App\ActivityLog;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class OrganisationsController extends Controller
{
public function index(){
$org_user = session('current_org_user');
// dd($org_user);
$merchants = Models\Merchant::where('org_id', $org_user['org_id'])->latest()->paginate(20);
$data = [
'merchants_list' => $merchants,
'merchants' => $merchants,
'page_title' => 'Merchants',
'org_user' => $org_user,
];
return view('organisation.merchants',$data);
}
public function landing(){
$org_user = session('current_org_user');
$merchants = Models\Merchant::get(); // to be used for tops in the dropdown
$dealer = Models\OrgUser::find($org_user['org_id']);
$merchant_ids = $merchants->pluck('id');
$merchant_topups = Models\MerchantTopUp::with('merchantInfo')->where('org_user_id', $org_user['id'])->orderBy('id', 'DESC')->get();
$data = [
'transaction_count' => $merchant_topups->count(),
'merchants' => $merchants,
'merchant_topups' => $merchant_topups->take(5),
'organisation' => $dealer,
'page_title' => 'Dashboard'
];
return view('organisation.landing',$data);
}
public function transactions(){
$org_user = session('current_org_user');
$data = [
// 'merchant_topups' => $merchant_topups,
'org_user' => $org_user,
'page_title' => 'Dealer Transactions'
];
return view('organisation.transactions', $data);
}
public function getTransactionsJson(Request $request){
$dealer_id = session('current_org_user.id');
$transactions_arr = \DB::table('merchant_top_ups')
->join('org_users', 'merchant_top_ups.org_user_id', '=', 'org_users.id')
->join('merchants', 'merchant_top_ups.merchant_id', '=', 'merchants.id')
->select('merchant_top_ups.id', 'merchant_top_ups.status', 'merchants.phone', 'merchants.fullname', 'merchant_top_ups.payment_method', 'merchant_top_ups.org_user_id', 'merchant_top_ups.amount', 'merchant_top_ups.created_at')
->whereRaw('merchant_top_ups.org_user_id = '. $dealer_id)
->orderBy('merchant_top_ups.created_at', 'DESC')
->paginate(10);
if($request->has('keyword')){
$queries = [];
$keyword = $request->keyword;
$queries['keyword'] = $keyword;
$transactions_arr = \DB::table('merchant_top_ups')
->join('org_users', 'merchant_top_ups.org_user_id', '=', 'org_users.id')
->join('merchants', 'merchant_top_ups.merchant_id', '=', 'merchants.id')
->select('merchant_top_ups.id', 'merchant_top_ups.status', 'merchants.phone', 'merchants.fullname', 'merchant_top_ups.payment_method', 'merchant_top_ups.org_user_id', 'merchant_top_ups.amount', 'merchant_top_ups.created_at')
->whereRaw("merchants.fullname LIKE '%$keyword%' OR merchants.phone LIKE '%$keyword%' OR merchant_top_ups.status LIKE '%$keyword%' OR merchant_top_ups.payment_method LIKE '%$keyword%' OR merchant_top_ups.amount LIKE '%$keyword%' OR merchant_top_ups.created_at LIKE '%$keyword%'")
->whereRaw('merchant_top_ups.org_user_id = '. $dealer_id)
->orderBy('merchant_top_ups.created_at', 'DESC')
->paginate(10)->appends($queries);
}
return response()->json($transactions_arr);
}
public function mytopups(){
$org_user = session('current_org_user');
$data = [
'org_user' => $org_user,
'page_title' => 'My Top Ups'
];
return view('organisation.mytopups', $data);
}
public function getMyTopUpsJson(Request $request){
$dealer_id = session('current_org_user.id');
$transactions_arr = \DB::table('organisation_top_ups')
->join('org_users', 'organisation_top_ups.org_id', '=', 'org_users.org_id')
->join('users', 'organisation_top_ups.user_id', '=', 'users.id')
->select('organisation_top_ups.id', 'organisation_top_ups.status', 'org_users.phone', 'users.name', 'organisation_top_ups.payment_method', 'organisation_top_ups.org_id', 'organisation_top_ups.amount', 'organisation_top_ups.created_at')
->whereRaw('organisation_top_ups.org_user_id = '. $dealer_id)
->orderBy('organisation_top_ups.created_at', 'DESC')
->paginate(10);
if($request->has('keyword')){
$queries = [];
$keyword = $request->keyword;
$queries['keyword'] = $keyword;
$transactions_arr = \DB::table('organisation_top_ups')
->join('org_users', 'organisation_top_ups.org_user_id', '=', 'org_users.id')
->join('users', 'organisation_top_ups.user_id', '=', 'users.id')
->select('organisation_top_ups.id', 'organisation_top_ups.status', 'org_users.phone', 'users.name', 'organisation_top_ups.payment_method', 'organisation_top_ups.org_id', 'organisation_top_ups.amount', 'organisation_top_ups.created_at')
->whereRaw("org_users.name LIKE '%$keyword%' OR org_users.phone LIKE '%$keyword%' OR organisation_top_ups.status LIKE '%$keyword%' OR organisation_top_ups.payment_method LIKE '%$keyword%' OR organisation_top_ups.amount LIKE '%$keyword%' OR organisation_top_ups.created_at LIKE '%$keyword%'")
->whereRaw('organisation_top_ups.org_user_id = '. $dealer_id)
->orderBy('organisation_top_ups.created_at', 'DESC')
->paginate(10)->appends($queries);
}
return response()->json($transactions_arr);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$merchant = Merchant::where('id',$id)->with('transactions')->get()[0];
$data = [
'merchant'=>$merchant,
];
return view('merchant.show',$data);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
$merchant = Merchant::findorfail($id);
$countries = Countries::all();
$data = [
'countries'=>$countries,
'merchant'=>$merchant,
];
return \view('merchant.update',$data);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->validate($request, [
'fullname' => 'required',
'phone_number'=> 'required',
'language' => 'required'
]);
$check_phone = $this->validatePhoneNumber($request->phone_number);
if ($check_phone == false) {
return redirect()->back()->withInput()->with('admin-error', 'Invalid phone number!');
}
Merchant::where('id', $id)->update([
'fullname'=> $request->fullname,
'phone'=> $check_phone,
'language' => $request->language
]);
$desc = [
'title' =>'updated a merchant',
'data' => $request->fullname,
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = $request->session()->get('current_org_user.id');
$activity->user_type = 'dealer';
$activity->description = json_encode($desc);
$activity->save();
return redirect()->to('/admin/merchant')->with('admin-sucsess', 'Successfully Updated Merchant');
}
public function merchantTopupOtpStore(Request $request){
$org_id = $request->session()->get('current_org_user.org_id');
$org_otp = random_int(1000, 9999);
$dealer = Models\OrgUser::where('org_id', $org_id)->first();
$smsgateway = new Smsgateway;
$request->session()->put('current_org_user.otp', $org_otp);
$msg = "Hello $dealer->name, your one time PIN is $org_otp. Enter this on the form to complete the top up.";
if(env('APP_ENV') !== 'local'){
$message_result = $smsgateway->sendSmsNew($dealer->phone, $msg);
}
\Log::info($msg);
//$this->sendNtfy($msg);
$lastThree = substr($dealer->phone, -3);
return response()->json([
'code' => 1,
'status' => 'success',
'message' => "An OTP has been sent to your phone number ending ... $lastThree. Enter this on the form to complete the top up."
]);
}
public function merchantTopupStore(Request $request)
{
$request->validate([
'merchant' => 'required|exists:merchants,id',
'amount' => 'required|numeric|min:1',
]);
$currentUser = $request->session()->get('current_org_user');
$orgId = $currentUser['org_id'];
$userId = $currentUser['id'];
$amount = (float) $request->amount;
$dealer = Models\OrgUser::findOrFail($orgId);
$merchant = Models\Merchant::findOrFail($request->merchant);
if ($amount > $dealer->balance) {
if ($request->expectsJson()) {
return response()->json([
'code' => 3,
'status' => 'failed',
'message' => 'Insufficient Balance.'
]);
}
Session::flash('success_message', 'Insufficient Balance!!');
return back()->withInput()->withErrors(['amount' => 'Insufficient Balance.']);
}
DB::beginTransaction();
try {
$topup = Models\MerchantTopUp::create([
'merchant_id' => $merchant->id,
// 'org_id' => $orgId,
'amount' => $amount,
'transaction_id' => 'MCH-' . Str::uuid(),
'org_user_id' => $userId,
'created_by' => $userId,
'payment_method' => 'cash'
]);
$merchant->increment('balance', $amount);
$dealer->decrement('balance', $amount);
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
\Log::error($e);
if ($request->expectsJson()) {
return response()->json([
'code' => 3,
'status' => 'failed',
'message' => 'Merchant Top Up failed.'
]);
}
Session::flash('error_message', 'Merchant Top Up failed!!');
return back()->withInput()->with('error', 'Merchant Top Up failed.');
}
ActivityLog::create([
'user_id' => $userId,
'user_type' => 'dealer',
'description' => json_encode([
'title' => 'dealer Added new Top Up',
'data' => 'MWK ' . number_format($amount, 2) . ' for ' . $merchant->fullname,
'type' => 'Dealer topup'
])
]);
$merchant->refresh();
try {
if (!app()->environment('local')) {
(new Smsgateway())->sendSmsNew(
$merchant->phone,
"Hello {$merchant->fullname}, your account has been credited with MWK {$amount}. Your new balance is MWK {$merchant->balance}."
);
}
} catch (\Exception $e) {
\Log::error($e);
}
if ($request->expectsJson()) {
return response()->json([
'code' => 1,
'status' => 'success',
'message' => 'Merchant Top Up Successfully completed.'
]);
}
Session::flash('success_message', 'Merchant Top Up Successfully completed!!');
return back()->with('success', 'Merchant Top Up Successfully completed.');
}
public function getActivity()
{
//
$org_user = session('current_org_user');
$activities = ActivityLog::with('admin')->where('user_type', 'dealer')
->where('user_id', $org_user['id'])
->orderBy('created_at', 'desc')
->paginate(20);
$data = [
'activities' => $activities,
'org_user' => $org_user,
'page_title' => 'Dealer Activity Log'
];
return view('organisation.activity', $data);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use PragmaRX\Countries\Package\Countries;
use Carbon\Carbon;
use App\ActivityLog;
use Mode\Models;
use App\Libs\Smsgateway;
class ResellersController extends Controller
{
public function index()
{
//
$merchants = Models\Reseller::latest()->paginate(10);
$data = [
'merchants'=>$merchants,
];
return view('resellers.index',$data);
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models;
use Session;
use DB;
use App\ActivityLog;
class SettingsController extends Controller
{
public function index()
{
$settings = Models\Settings::get();
// dd($settings);
$data = [
'settings' => $settings,
'page_title' => 'System Settings'
];
return view('super_admin.settings',$data);
}
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
'value' => 'required',
'id' => 'required'
]);
$name = $request->input('name');
$value = $request->input('value');
$settings = Models\Settings::find($id);
if ($settings) {
$settings->name = $value;
$settings->save();
} else {
Models\Settings::create([
'name' => $name,
'value' => $value
]);
}
return redirect()->back()->with('success', 'Settings updated successfully!');
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models;
use Validator;
use Carbon\Carbon;
use App\ActivityLog;
use Illuminate\Support\Facades\Auth;
use App\Libs\Smsgateway;
class TopUpsController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$topups = Models\OrganisationTopUp::with('orgInfo','userInfo')->latest()->paginate(10);
$data = [
'topups' => $topups,
'page_title' => 'org_topup_list'
];
return view('super_admin.topups',$data);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$organisations = Models\Organisation::get();
$data = [
'organisations' => $organisations,
'page_title' => 'org_topups'
];
return view('topups.create',$data);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, [
'amount' => 'required',
'payment' => 'required',
'organisation' => 'required',
'discount' => 'required|numeric'
]);
// validate json request in laravel
$organisation = Models\Organisation::findOrFail($request->organisation);
$current_balance = $organisation->balance;
$discount = 1 + ($request->discount/100);
$new_amount = (float)$request->amount * $discount;
$topup = new Models\OrganisationTopUp;
$topup->amount = round($new_amount, 2);
$topup->current_balance = $organisation->balance;
$topup->org_id = $request->organisation;
$topup->created_by = Auth::user()->id;
$topup->user_id = Auth::user()->id;
$topup->payment_method = $request->payment;
$save = $topup->save();
//Update Organisation Amt
$retval = Models\Organisation::where('id', $request->organisation)->increment('balance', $new_amount);
if ($save == true && $retval == true) {
$desc = [
'title'=>'Account Top Up',
'data'=>'MWK'. $request->amount.' for '.$organisation->name.' payment by '. $request->payment,
'type'=>'topup',
];
$activity = new ActivityLog;
$activity->user_id = Auth::user()->id;
$activity->user_type = 'admin'; // Auth::user()->id;
$activity->description = json_encode($desc);
$activity->save();
$smsgateway = new Smsgateway;
$organisation = Models\Organisation::findOrFail($request->organisation);
$organisation_phone = $organisation->phone;
$organisation_name = $organisation->name;
$organisation_new_balance = $organisation->balance;
$msg = "Hello $organisation_name, your account has been credited with $new_amount Kwacha and your new balance is : $organisation_new_balance";
if (env('APP_ENV') != 'local') {
$message_result = $smsgateway->sendSmsNew($organisation_phone, $msg);
}
\Log::info($msg);
if($request->ajax()){{
return response()->json([
'status' => 'success',
'code' => 1,
'message' => 'Top Up Successful'
]);
}
}
else{
return redirect()->to('/admin/topups')->with('admin-sucsess', 'Successfully Added New Top Up');
}
}
else {
if($request->ajax()){{
return response()->json([
'status' => 'fail`',
'code' => 3,
'message' => 'Top Up Failed'
]);
}
}
else{
return redirect()->back()->with('admin-error', 'Error');
}
}
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Transaction;
class TransactionController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$transactions = Transaction::with('merchant')->latest()->paginate(30);
$data = [
'transactions'=>$transactions,
];
return \view('transactions.index',$data);
}
}

View File

@@ -0,0 +1,165 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use App\ActivityLog;
use Illuminate\Support\Facades\Auth;
use Validator;
use Illuminate\Support\Facades\Mail;
use App\Mail\UserRegistration;
use Illuminate\Support\Facades\Hash;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//where('status','Active')->
$users = User::where('status','Active')->latest()->paginate(10);
$data = [
'users' => $users,
'page_title' => 'Admin Users',
];
return view('users.index',$data);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$data = [
'page_title' => 'New Admin User'
];
return view('users.create', $data);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
\Log::info('here at the wall');
// todo : form filler password : Pa$$w0rd!
$this->validate($request, [
'name' => 'required',
'email'=> 'required|unique:users',
'password' => 'required|confirmed'
]);
// dd($request->all());
// Hash::make($data['password'])
$user = new User;
$user->name = $request->name;
$user->email = $request->email;
$user->password = Hash::make($request->password) ;
$user->save();
//sending the mail
$newUser = [
'name' => $request->name,
'email' => $request->email,
];
$email = $request->email;
Mail::to($email)->send(new UserRegistration($newUser));
$desc = [
'title' => 'Created a user',
'data' => $request->name,
'type' => 'user',
];
$activity = new ActivityLog;
$activity->user_id = Auth::user()->id;
$activity->user_type = 'admin';
$activity->description = json_encode($desc);
$activity->save();
return redirect()->to('/admin/users')->with('admin-sucsess', 'Successfully Added New User');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$user = User::findOrFail($id);
$data = [
'user' => $user,
'page_title' => 'list_create'
];
return view('users.update', $data);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
$user = User::where('id', $id)->update([
'status'=>'disabled',
]);
$user = User::find($id);
$desc = [
'title'=> 'disabled a user with ID ' . $user->id,
'data'=> $user->name,
'type'=> 'user',
];
$activity = new ActivityLog;
$activity->user_id = Auth::user()->id;
$activity->user_type = 'admin'; //Auth::user()->id;
$activity->description = json_encode($desc);
$activity->save();
return redirect()->to('/admin/users')->with('admin-sucsess', 'Successfully Disabled User');
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;
use App\Models;
use Session;
use App\Libs\Smsgateway;
use Carbon\Carbon;
use App\ActivityLog;
use Validator;
class UtilityController extends Controller
{
public function index(){
\Log::info('here at the wall : users view');
//where('status','Active')->
// $users = User::where('status','Active')->latest()->paginate(10);
// $data = [
// 'users' => $users,
// ];
// //
// return view('users.index',$data);
}
public function customLogExample()
{
Log::channel('custom')->info('This is a custom log message.');
return response()->json(['message' => 'Custom log written successfully!']);
}
public function mattermosttest()
{
// log::info('This is a test message for Mattermost logging.');
// logger()->error('Some message', [
// 'context' => 'Some contex',
// 'an_array_of_things' => ['foo', 'bar', 'baz'],
// ]);
Log::channel('mattermost')->info("mattermost log test message");
// Or
// throw new \Exception('An exception occured');
Log::error('Something really important');
// Log::channel('mattermost')->info('This is a test message for Mattermost logging.');
return response()->json(['message' => 'Mattermost log written successfully!']);
}
public function refundStore(Request $request){
$validatedData = $request->validate([
'dealer_id' => 'required',
'merchant_id' => 'required',
'amount' => 'required|numeric',
'reason' => 'required',
]);
$merchant = Models\Merchant::findorfail($request->merchant_id);
$merchant_name = $merchant->fullname;
$dealer = Models\OrgUser::findorfail($request->dealer_id);
$dealer_name = $dealer->name;
$organisation = Models\Organisation::findorfail($dealer->org_id);
$result = Models\MerchantToDealerRefund::create([
'dealer_id' => $request->dealer_id,
'merchant_id' => $request->dealer_id,
'amount' => $request->amount,
'dealer_msisdn' => $dealer->phone,
'reason' => $request->reason,
]);
$merchant_balance = $merchant->balance;
$merchant->balance = $merchant_balance - $request->amount;
$merchant->save();
//Review this -- should dealers belong to a larger organisation or they should be on their own
$dealer_balance = $organisation->balance;
$organisation->balance = $dealer_balance + $request->amount;
$organisation->save();
$desc = [
'title' => 'Dealer Refund',
'data' => "Redunded a dealer with an amount of " . $request->amount,
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = $request->session()->get('current_org_user.id'); // org_user_id ;
$activity->user_type = 'merchant';
$activity->description = json_encode($desc);
$activity->save();
$msg = "You have been Successfully refunded a dealer ($dealer_name) with an amount of $request->amount.";
$dealer_msg = "You have received a refund amount of $request->amount from Airtime Merchant ($merchant_name)";
$smsgateway = new Smsgateway;
if(env('APP_ENV') !== 'local'){
$result = $smsgateway->sendSmsNew($merchant->phone, $msg);
$result = $smsgateway->sendSmsNew($dealer->phone, $dealer_msg);
}
\Log::info($msg);
\Log::info($dealer_msg);
// $this->sendNtfy($msg);
if($request->ajax()){
return response()->json([
'code' => 1,
'status' => 'success',
'message' => 'Dealer Refund Successfully processed'
]);
}
else{
return redirect()->to('merchant/refunds')->with('success_message', 'Dealer Refund Successfully processed');
}
}
}

77
app/Http/Kernel.php Normal file
View File

@@ -0,0 +1,77 @@
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, class-string|string>
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'login'=>\App\Http\Middleware\Login::class,
'logout'=>\App\Http\Middleware\Logout::class,
'admin'=>\App\Http\Middleware\AuthUser::class,
'merchant'=>\App\Http\Middleware\Merchant::class,
'organisation'=>\App\Http\Middleware\Organisation::class,
'merchant-login'=>\App\Http\Middleware\MerchantLogin::class,
'merchant-logout'=>\App\Http\Middleware\MerchantLogout::class,
];
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class AuthUser
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if(! Auth::check()){
return redirect()->to('/login')->with('login-error','You need to Login to gain access');
}
return $next($request);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
/*
protected function redirectTo($request)
{
dd($request->expectsJson());
if (! $request->expectsJson()) {
return route('login');
}
else{
return route('api');
}
}
*/
protected function redirectTo($request) {
return $request->expectsJson() || str_starts_with($request->path(), 'api/') ? null : '/login'; // Adjust to your admin path
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

28
app/Http/Middleware/Login.php Executable file
View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class Login
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if(Auth::check()){
return redirect()->to('/');
}
return $next($request);
}
}

26
app/Http/Middleware/Logout.php Executable file
View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class Logout
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if(! Auth::check()){
return redirect()->to('/login');
}
return $next($request);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use Closure;
class Merchant
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$merchant = session('merchant');
if ($merchant == null) {
return redirect()->to('/merchant/login')->with('login-error', 'Login to Gain Access!');
}
return $next($request);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use Closure;
class MerchantLogin
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$merchant = session('merchant');
if ($merchant !== null) {
return redirect()->to('/merchant');
}
return $next($request);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use Closure;
class MerchantLogout
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$merchant = session('merchant');
if ($merchant == null) {
return redirect()->to('/merchant/login')->with('login-error', 'Login to Gain Access!');
}
return $next($request);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Closure;
class Organisation
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
// public function handle($request, Closure $next)
// {
// return $next($request);
// }
public function handle($request, Closure $next)
{
// $merchant = session('merchant');
if(!$request->session()->has('current_org_user')){
return redirect(url('/organisation/login'))->withErrors("You need to be logged in");
}
return $next($request);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @param string|null ...$guards
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Session\Store;
class SessionTimeout{
protected $session;
protected $timeout = 600;
public function __construct(Store $session){
$this->session = $session;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
if( !$this->session->has('lastActivityTime') ) {
$this->session->put('lastActivityTime', time());
}
elseif ( time() - $this->session->get('lastActivityTime') > $this->timeout ) {
$this->session->forget('lastActivityTime');
#\App\Http\Controllers\LoginController::merchant_logout($request);
$cookie = \Cookie::forget('fortunae');
return redirect("/")->withErrors(['You had no activity over the past '. $this->timeout/60 .' minutes.'])->withCookie($cookie);
}
$this->session->put('lastActivityTime',time());
return $next($request);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,93 @@
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class LoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => ['required', 'string', 'email'],
'password' => ['required', 'string'],
];
}
/**
* Attempt to authenticate the request's credentials.
*
* @return void
*
* @throws \Illuminate\Validation\ValidationException
*/
public function authenticate()
{
$this->ensureIsNotRateLimited();
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
/**
* Ensure the login request is not rate limited.
*
* @return void
*
* @throws \Illuminate\Validation\ValidationException
*/
public function ensureIsNotRateLimited()
{
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
return;
}
event(new Lockout($this));
$seconds = RateLimiter::availableIn($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
]);
}
/**
* Get the rate limiting throttle key for the request.
*
* @return string
*/
public function throttleKey()
{
return Str::lower($this->input('email')).'|'.$this->ip();
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Imports;
use App\User;
use Maatwebsite\Excel\Concerns\ToModel;
class PropertiesImport implements ToModel
{
private $rows = 0;
public function model(array $row)
{
++$this->rows;
return new User([
'name' => $row[0],
]);
}
public function getRowCount(): int
{
return $this->rows;
}
}
?>

33
app/Libs/Ntfysender.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
namespace App\Libs;
use Config;
use Log;
use App;
class Ntfysender {
function sendNtfy($data){
$url = 'https://ntfy.sh/airtimeSalesPortal';
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json'
),
CURLOPT_URL => $url,
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;
}
}
?>

64
app/Libs/Smsgateway.php Executable file
View File

@@ -0,0 +1,64 @@
<?php
namespace App\Libs;
use Config;
use Log;
use App;
class Smsgateway {
function sendSms($msisdn, $message){
$url ="http://10.199.0.10/ucm_api/index.php";
$data = array (
'reference' => uniqid('_airtime'),
'message_type' => "1",
'message' => $message,
'user_id' => "288",
'app_id' => "600118",
'org_id' => "82",
'src_address' => "349",
'dst_address' => $msisdn,
'service_id' => "",
'operation' => "send",
'timestamp' => preg_replace('/\D/', '', date("YYYYmmddHHiiss")),
'auth_key' => md5(600118 . date("YYYYmmddHHiiss") . "!QAZ2wsx*")
);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
$ucm_response = curl_exec($curl);
@file_put_contents("/var/www/html/malawi/airtime_sales_portal/public/logs/" . date("Y_m_d_") . "sms_responses.txt", $ucm_response . PHP_EOL, FILE_APPEND);
return $ucm_response;
}
function sendSmsNew($msisdn, $message){
$url = "http://10.199.0.10:8989/api/messaging/sendsms";
$token = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI4MiIsIm9pZCI6ODIsInVpZCI6IjRkNTUzZTZhLTBmYzAtNDJjNy05MWMyLTk5ZmUzNDliNWFmYSIsImFwaWQiOjkxLCJpYXQiOjE2NDg0NjQxOTQsImV4cCI6MTk4ODQ2NDE5NH0.Nndr6MshNvpiYoqv3aLI_sw9kSZP1dUUBa-aWQ1y0iHlkLe0DhLZymUl430IBVOOGI--Rl_ur1JEAJ-8BRhS2w";
$data = array (
'from' => "349",
'to' => $msisdn,
'message' => $message,
'refId' => uniqid('_airtime')
);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
"Authorization: Bearer $token",
"Content-type: application/json"
));
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
$ucm_response = curl_exec($curl);
@file_put_contents("/var/www/html/malawi/airtime_sales_portal/public/logs/" . date("Y_m_d_") . "sms_responses.txt", $ucm_response . PHP_EOL, FILE_APPEND);
return $ucm_response;
}
}
?>

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Logging;
use Monolog\Logger;
class CustomLogger
{
/**
* Create a custom Monolog instance.
*
* @param array $config
*
* @return \Monolog\Logger
*/
public function __invoke(array $config)
{
return new Logger(
env('APP_NAME'),
[
// new MattermostHandler(
// $config['url'],
// $config['level']
// ),
]
);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Logging;
use Monolog\Handler\AbstractProcessingHandler;
use Snowfire\Beautymail\BeautymailFacade;
use Libs\Ntfsender;
class CustomLoggerHandler extends AbstractProcessingHandler
{
public function write(array $record)
{
BeautymailFacade::send('emails.my_mail', ['record' => $record], function ($message) {
$message->from('privacy@gmail.com')
->to("hello@gmail.com")
->subject("Email Log");
});
// $ntfy = new Ntfysender();
// $ntfy->sendNtfy(json_encode($record));
\Log::info("yoyo");
\Log::info($record['message']);
\Log::info(json_encode($record));
\Log::info("yoyo");
}
}
?>

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class UserRegistration extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct($orgUser, $otp)
{
$this->orgUser = $orgUser;
$this->otp = $otp;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
// $orgUser = $this->orgUser;
// return $this->view('view.name');
return $this->from('your_email@example.com')
->subject('Airtime Sales Portal OTP Code')
->view('emails.dealer_topup_otp')
->with('details', $this->orgUser)
->with('otp', $this->otp);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class UserRegistration extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct($newUser)
{
$this->newUser = $newUser;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
// return $this->view('view.name');
return $this->from('your_email@example.com')
->subject('Welcome to Airtime Sales Portal')
->view('emails.newuser')
->with('details', $this->newUser);
}
}

10
app/Models/Balance.php Executable file
View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Balance extends Model
{
//
}

30
app/Models/Merchant.php Executable file
View File

@@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Merchant extends Model
{
//
// protected $fillable = [
// 'fullname', 'phone', 'pin','country', 'language', 'org_id'
// ];
protected $guarded = [
'id'
];
public function transactions()
{
return $this->hasMany('App\Models\Transaction', 'merchant_id', 'id')->orderBy('created_at','desc')->take(10);
}
public function topups()
{
return $this->hasMany('App\Models\Topup', 'merchant_id', 'id')->orderBy('created_at','desc')->take(10);
}
public function orgInfo()
{
return $this->belongsTo('App\Models\Organisation', 'org_id', 'id');
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class MerchantToDealerRefund extends Model
{
use HasFactory;
protected $guarded = [
'id'
];
public $table = "merchant_to_dealer_refunds";
public function merchantInfo()
{
return $this->hasOne('App\Models\Merchant', 'id', 'merchant_id');
}
public function orgUserInfo()
{
return $this->hasOne('App\Models\OrgUser', 'id', 'dealer_id');
}
public function orgInfo()
{
return $this->hasOne('App\Models\Organisation', 'id', 'dealer_id');
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class MerchantTopUp extends Model
{
public $table = "merchant_top_ups";
protected $guarded = ['id'];
public function merchantInfo()
{
return $this->hasOne('App\Models\Merchant', 'id', 'merchant_id');
}
public function orgUserInfo()
{
return $this->hasOne('App\Models\OrgUser', 'id', 'org_user_id');
}
public function orgInfo()
{
return $this->hasOne('App\Models\Organisation', 'id', 'org_id');
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ModelsSettings extends Model
{
use HasFactory;
}

16
app/Models/OrgUser.php Executable file
View File

@@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class OrgUser extends Model{
public $table = "org_users";
protected $guarded = ['id'];
public function org_info(){
return $this->hasOne('App\Models\Organisation', 'id', 'org_id');
// return $this->belongsTo(Organisation::class);
}
}

22
app/Models/Organisation.php Executable file
View File

@@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Organisation extends Model
{
protected $guarded = [
'id'
];
public function orgUserInfo()
{
return $this->hasMany('App\Models\OrgUser', 'org_id', 'id');
}
public function userInfo()
{
return $this->hasOne('App\User', 'id', 'user_id');
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class OrganisationTopUp extends Model
{
public $table = "organisation_top_ups";
protected $guarded = [
'id'
];
public function orgInfo()
{
return $this->hasOne('App\Models\Organisation', 'id', 'org_id');
}
public function UserInfo()
{
return $this->hasOne('App\User', 'id', 'user_id');
}
}

10
app/Models/Reseller.php Executable file
View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Reseller extends Model
{
//
}

10
app/Models/Service.php Executable file
View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Service extends Model
{
//
}

16
app/Models/Settings.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Settings extends Model
{
use HasFactory;
public $table = "settings";
protected $guarded = [
'id'
];
}

10
app/Models/SuperAdmin.php Executable file
View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class SuperAdmin extends Model
{
//
}

23
app/Models/Topup.php Executable file
View File

@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Topup extends Model
{
public $table = "top_ups";
public function merchant()
{
return $this->belongsTo('App\Models\Merchant', 'merchant_id', 'id');
}
public function admin()
{
return $this->belongsTo('App\User', 'created_by', 'id');
}
}

17
app/Models/Transaction.php Executable file
View File

@@ -0,0 +1,17 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Transaction extends Model
{
//
protected $guarded = [
'id'
];
public function merchant()
{
return $this->belongsTo('App\Models\Merchant', 'merchant_id', 'id');
}
}

45
app/Models/User.php Normal file
View File

@@ -0,0 +1,45 @@
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'status'
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Libs\Ntfysender;
use Opcodes\LogViewer\Facades\LogViewer;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//\Schema::defaultStringLength(191);
// $monolog = \Log::getMonolog();
//$monolog = \Log::getLogger();
//new --- > xoxp-677819906294-675678554016-693747277911-5166bcbb9a5fc3d5434b42a10c4d358a
//clickml_erp_ntfy
//click_erp_notify
// $slackHandler = new \Monolog\Handler\SlackHandler('xoxp-677819906294-675678554016-693747277911-5166bcbb9a5fc3d5434b42a10c4d358a', '#clickml_erp_ntfy', 'Monolog', true, null, \Monolog\Logger::ERROR);
// $slackHandler = new \Monolog\Handler\SlackHandler('xoxp-677819906294-675678554016-693747277911-5166bcbb9a5fc3d5434b42a10c4d358a', '#clickml_erp_ntfy', 'Monolog', true, null, \Monolog\Logger::INFO);
// $slackHandler = new \Monolog\Handler\SlackHandler('xoxp-677819906294-675678554016-693747277911-5166bcbb9a5fc3d5434b42a10c4d358a', '#clickml_erp_ntfy', 'Monolog', true, null, \Monolog\Logger::DEBUG);
// $ntfy_handler = new Ntfysender(\Monolog\Logger::ERROR);
// $ntfy_handler = new Ntfysender(\Monolog\Logger::INFO);
// $ntfy_handler = new Ntfysender(\Monolog\Logger::DEBUG);
// dump($ntfy_handler);
// $monolog->pushHandler($ntfy_handler);
LogViewer::auth(function ($request) {
// return true to allow viewing the Log Viewer.
return true;
});
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
//
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to the "home" route for your application.
*
* This is used by Laravel authentication to redirect users after login.
*
* @var string
*/
public const HOME = '/dashboard';
/**
* The controller namespace for the application.
*
* When present, controller route declarations will automatically be prefixed with this namespace.
*
* @var string|null
*/
protected $namespace = 'App\\Http\\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
});
}
/**
* Configure the rate limiters for the application.
*
* @return void
*/
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
});
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class PhoneValidationRule implements Rule
{
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $phone)
{
$phone = str_replace(' ', '', $phone);
$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 $msisdn;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'Phone number is not valid';
}
}

40
app/User.php Executable file
View File

@@ -0,0 +1,40 @@
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use Notifiable, HasApiTokens ;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password','status'
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class AppLayout extends Component
{
/**
* Get the view / contents that represents the component.
*
* @return \Illuminate\View\View
*/
public function render()
{
return view('layouts.app');
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class GuestLayout extends Component
{
/**
* Get the view / contents that represents the component.
*
* @return \Illuminate\View\View
*/
public function render()
{
return view('layouts.guest');
}
}

79
app/bits-pieces.php Normal file
View File

@@ -0,0 +1,79 @@
<?php
public function merchantTopupStoreOld(Request $request){
$this->validate($request, [
'merchant'=> 'required',
'amount' => 'required|numeric|min:1'
]);
$org_id = $request->session()->get('current_org_user.org_id');
$organisation = Models\Organisation::find($org_id);
$prev_balance = $organisation->balance;
$merchant = Models\Merchant::findOrFail($request->merchant);
$current_balance = $merchant->balance;
if ( $request->amount > $organisation->balance ) {
return response()->json([
'code' => 3,
'status' => 'failed',
'message' => 'Insufficient Balance!'
]);
}
// $discount = 1 + ($request->discount/100);
// $new_amount = (float)$request->amount * $discount;
$topup = new Models\MerchantTopUp;
$topup->amount = $request->amount;
$topup->merchant_id = $request->merchant;
$topup->org_id = $org_id;
$topup->transaction_id = random_int(10000000, 99999999);;
$topup->org_user_id = $request->session()->get('current_org_user.id');
$topup->created_by = $request->session()->get('current_org_user.id');
$topup->payment_method = 'cash';
$save = $topup->save();
//Update Merchant Amt
$retval = Models\Merchant::where('id', $request->merchant)->increment('balance', $request->amount);
$result = Models\Organisation::where('id', $org_id)->decrement('balance', $request->amount);
if ($save == true && $retval == true && $result) {
$desc = [
'title'=>'Org User Added new Top Up',
'data'=>'MWK'. $request->amount .' for ' . $merchant->fullname,
'type'=>'org_user topup',
];
$activity = new ActivityLog;
$activity->user_id = $request->session()->get('current_org_user.id');
$activity->user_type = 'organisation';
$activity->description = json_encode($desc);
$activity->save();
$smsgateway = new Smsgateway;
$merchant = Models\Merchant::findOrFail($request->merchant);
$merchant_phone = $merchant->phone;
$merchant_name = $merchant->fullname;
$merchant_new_balance = $merchant->balance;
$msg = "Hello $merchant_name, your account has been credited with $request->amount Kwacha and your new balance is : $merchant_new_balance";
if(env('APP_ENV') !== 'local'){
$message_result = $smsgateway->sendSmsNew($merchant_phone, $msg);
}
\Log::info($msg);
return response()->json([
'code' => 1,
'status' => 'success',
'message' => 'Merchant Top Up Successfully completed!'
]);
} else {
return response()->json([
'code' => 3,
'status' => 'failed',
'message' => 'Merchant Top Up failed!'
]);
}
}
?>

53
artisan Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env php
<?php
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any of our classes manually. It's great to relax.
|
*/
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
new Symfony\Component\Console\Output\ConsoleOutput
);
/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/
$kernel->terminate($input, $status);
exit($status);

65
bits-pieces.md Normal file
View File

@@ -0,0 +1,65 @@
public function refundStoreOld(Request $request){
$validatedData = $request->validate([
'dealer_id' => 'required',
'merchant_id' => 'required',
'amount' => 'required|numeric',
'transaction_id' => 'required|string',
'reason' => 'required|string',
]);
// dd($request->all());
$merchant = Models\Merchant::findorfail($request->merchant_id);
// dd($merchant);
$merchant_name = $merchant->fullname;
$dealer = Models\OrgUser::findorfail($request->dealer_id);
$dealer_name = $dealer->name;
$organisation = Models\Organisation::findorfail($request->dealer_id);
$result = Models\MerchantToDealerRefund::create([
'dealer_id' => $request->dealer_id,
'merchant_id' => $request->merchant_id,
'amount' => $request->amount,
'dealer_msisdn' => $dealer->phone,
'reason' => $request->reason,
]);
$merchant_balance = $merchant->balance;
$merchant->balance = $merchant_balance - $request->amount;
$merchant->save();
//Review this -- should dealers belong to a larger organisation or they should be on their own
$dealer_balance = $organisation->balance;
$organisation->balance = $dealer_balance + $request->amount;
$organisation->save();
$desc = [
'title' => 'Dealer Refund',
'data' => "Redunded a dealer with an amount of " . $request->amount,
'type' => 'merchant',
];
$activity = new ActivityLog;
$activity->user_id = $request->session()->get('current_org_user.id'); // org_user_id ;
$activity->user_type = 'merchant';
$activity->description = json_encode($desc);
$activity->save();
$msg = "You have been Successfully refunded a dealer ($dealer_name) with an amount of $request->amount.";
$dealer_msg = "You have received a refund amount of $request->amount from Airtime Merchant ($merchant_name)";
$smsgateway = new Smsgateway;
if(env('APP_ENV') !== 'local'){
$result = $smsgateway->sendSmsNew($merchant->phone, $msg);
$result = $smsgateway->sendSmsNew($dealer->phone, $dealer_msg);
}
\Log::info($msg);
\Log::info($dealer_msg);
// $this->sendNtfy($msg);
if($request->ajax()){
return response()->json([
'code' => 1,
'status' => 'success',
'message' => 'Dealer Refund Successfully processed'
]);
}
else{
return redirect()->to('merchant/refunds')->with('success_message', 'Dealer Refund Successfully processed');
}
}

55
bootstrap/app.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
|
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.
|
*/
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
/*
|--------------------------------------------------------------------------
| Return The Application
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
|
*/
return $app;

68
composer.json Normal file
View File

@@ -0,0 +1,68 @@
{
"name": "laravel/laravel",
"type": "project",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"require": {
"php": "^7.3|^8.0",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/breeze": "1.9.2",
"laravel/framework": "^8.75",
"laravel/sanctum": "^2.15",
"laravel/tinker": "^2.5",
"maatwebsite/excel": "^1.1",
"opcodesio/log-viewer": "^3.18",
"symfony/google-mailer": "^6.4",
"symfony/mailgun-mailer": "^6.2",
"symfony/sendgrid-mailer": "^6.1"
},
"require-dev": {
"facade/ignition": "^2.5",
"fakerphp/faker": "^1.9.1",
"laravel/sail": "^1.0.1",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^5.10",
"phpunit/phpunit": "^9.5.10"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

8324
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

8019
composer.lock.bak Normal file

File diff suppressed because it is too large Load Diff

236
config/app.php Normal file
View File

@@ -0,0 +1,236 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application. This value is used when the
| framework needs to place the application's name in a notification or
| any other location as required by the application or its packages.
|
*/
'name' => env('APP_NAME', 'Laravel'),
/*
|--------------------------------------------------------------------------
| Application Environment
|--------------------------------------------------------------------------
|
| This value determines the "environment" your application is currently
| running in. This may determine how you prefer to configure various
| services the application utilizes. Set this in your ".env" file.
|
*/
'env' => env('APP_ENV', 'production'),
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => (bool) env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| your application so that it is used when running Artisan tasks.
|
*/
'url' => env('APP_URL', 'http://localhost'),
'asset_url' => env('ASSET_URL', null),
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. We have gone
| ahead and set this to a sensible default for you out of the box.
|
*/
'timezone' => 'UTC',
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/
'locale' => 'en',
/*
|--------------------------------------------------------------------------
| Application Fallback Locale
|--------------------------------------------------------------------------
|
| The fallback locale determines the locale to use when the current one
| is not available. You may change the value to correspond to any of
| the language folders that are provided through your application.
|
*/
'fallback_locale' => 'en',
/*
|--------------------------------------------------------------------------
| Faker Locale
|--------------------------------------------------------------------------
|
| This locale will be used by the Faker PHP library when generating fake
| data for your database seeds. For example, this will be used to get
| localized telephone numbers, street address information and more.
|
*/
'faker_locale' => 'en_US',
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| This key is used by the Illuminate encrypter service and should be set
| to a random, 32 character string, otherwise these encrypted strings
| will not be safe. Please do this before deploying an application!
|
*/
'key' => env('APP_KEY'),
'cipher' => 'AES-256-CBC',
/*
|--------------------------------------------------------------------------
| Autoloaded Service Providers
|--------------------------------------------------------------------------
|
| The service providers listed here will be automatically loaded on the
| request to your application. Feel free to add your own services to
| this array to grant expanded functionality to your applications.
|
*/
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
/*
* Package Service Providers...
*/
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
],
/*
|--------------------------------------------------------------------------
| Class Aliases
|--------------------------------------------------------------------------
|
| This array of class aliases will be registered when this application
| is started. However, feel free to register as many as you wish as
| the aliases are "lazy" loaded so they don't hinder performance.
|
*/
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Arr' => Illuminate\Support\Arr::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
'Blade' => Illuminate\Support\Facades\Blade::class,
'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
'Bus' => Illuminate\Support\Facades\Bus::class,
'Cache' => Illuminate\Support\Facades\Cache::class,
'Config' => Illuminate\Support\Facades\Config::class,
'Cookie' => Illuminate\Support\Facades\Cookie::class,
'Crypt' => Illuminate\Support\Facades\Crypt::class,
'Date' => Illuminate\Support\Facades\Date::class,
'DB' => Illuminate\Support\Facades\DB::class,
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
'Event' => Illuminate\Support\Facades\Event::class,
'File' => Illuminate\Support\Facades\File::class,
'Gate' => Illuminate\Support\Facades\Gate::class,
'Hash' => Illuminate\Support\Facades\Hash::class,
'Http' => Illuminate\Support\Facades\Http::class,
'Js' => Illuminate\Support\Js::class,
'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class,
'Notification' => Illuminate\Support\Facades\Notification::class,
'Password' => Illuminate\Support\Facades\Password::class,
'Queue' => Illuminate\Support\Facades\Queue::class,
'RateLimiter' => Illuminate\Support\Facades\RateLimiter::class,
'Redirect' => Illuminate\Support\Facades\Redirect::class,
// 'Redis' => Illuminate\Support\Facades\Redis::class,
'Request' => Illuminate\Support\Facades\Request::class,
'Response' => Illuminate\Support\Facades\Response::class,
'Route' => Illuminate\Support\Facades\Route::class,
'Schema' => Illuminate\Support\Facades\Schema::class,
'Session' => Illuminate\Support\Facades\Session::class,
'Storage' => Illuminate\Support\Facades\Storage::class,
'Str' => Illuminate\Support\Str::class,
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'Excel' => Maatwebsite\Excel\Facades\Excel::class,
],
];

115
config/auth.php Normal file
View File

@@ -0,0 +1,115 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'sanctum',
'provider' => 'users',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];

64
config/broadcasting.php Normal file
View File

@@ -0,0 +1,64 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "pusher", "ably", "redis", "log", "null"
|
*/
'default' => env('BROADCAST_DRIVER', 'null'),
/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over websockets. Samples of
| each available type of connection are provided inside this array.
|
*/
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'useTLS' => true,
],
],
'ably' => [
'driver' => 'ably',
'key' => env('ABLY_KEY'),
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
],
];

110
config/cache.php Normal file
View File

@@ -0,0 +1,110 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Cache Store
|--------------------------------------------------------------------------
|
| This option controls the default cache connection that gets used while
| using this caching library. This connection is used when another is
| not explicitly specified when executing a given caching function.
|
*/
'default' => env('CACHE_DRIVER', 'file'),
/*
|--------------------------------------------------------------------------
| Cache Stores
|--------------------------------------------------------------------------
|
| Here you may define all of the cache "stores" for your application as
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "apc", "array", "database", "file",
| "memcached", "redis", "dynamodb", "octane", "null"
|
*/
'stores' => [
'apc' => [
'driver' => 'apc',
],
'array' => [
'driver' => 'array',
'serialize' => false,
],
'database' => [
'driver' => 'database',
'table' => 'cache',
'connection' => null,
'lock_connection' => null,
],
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
],
'memcached' => [
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
],
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
'octane' => [
'driver' => 'octane',
],
],
/*
|--------------------------------------------------------------------------
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| When utilizing a RAM based store such as APC or Memcached, there might
| be other applications utilizing the same cache. So, we'll specify a
| value to get prefixed to all our keys so we can avoid collisions.
|
*/
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'),
];

34
config/cors.php Normal file
View File

@@ -0,0 +1,34 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];

147
config/database.php Normal file
View File

@@ -0,0 +1,147 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for all database work. Of course
| you may use many connections at once using the Database library.
|
*/
'default' => env('DB_CONNECTION', 'mysql'),
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DATABASE_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix_indexes' => true,
'schema' => 'public',
'sslmode' => 'prefer',
],
'sqlsrv' => [
'driver' => 'sqlsrv',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '1433'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix_indexes' => true,
],
],
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run in the database.
|
*/
'migrations' => 'migrations',
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer body of commands than a typical key-value system
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
*/
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
],
],
];

73
config/filesystems.php Normal file
View File

@@ -0,0 +1,73 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| Here you may specify the default filesystem disk that should be used
| by the framework. The "local" disk, as well as a variety of cloud
| based disks are available to your application. Just store away!
|
*/
'default' => env('FILESYSTEM_DRIVER', 'local'),
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Here you may configure as many filesystem "disks" as you wish, and you
| may even configure multiple disks of the same driver. Defaults have
| been setup for each driver as an example of the required options.
|
| Supported Drivers: "local", "ftp", "sftp", "s3"
|
*/
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
],
],
/*
|--------------------------------------------------------------------------
| Symbolic Links
|--------------------------------------------------------------------------
|
| Here you may configure the symbolic links that will be created when the
| `storage:link` Artisan command is executed. The array keys should be
| the locations of the links and the values should be their targets.
|
*/
'links' => [
public_path('storage') => storage_path('app/public'),
],
];

52
config/hashing.php Normal file
View File

@@ -0,0 +1,52 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Hash Driver
|--------------------------------------------------------------------------
|
| This option controls the default hash driver that will be used to hash
| passwords for your application. By default, the bcrypt algorithm is
| used; however, you remain free to modify this option if you wish.
|
| Supported: "bcrypt", "argon", "argon2id"
|
*/
'driver' => 'bcrypt',
/*
|--------------------------------------------------------------------------
| Bcrypt Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Bcrypt algorithm. This will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 10),
],
/*
|--------------------------------------------------------------------------
| Argon Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Argon algorithm. These will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'argon' => [
'memory' => 65536,
'threads' => 1,
'time' => 4,
],
];

320
config/log-viewer.php Normal file
View File

@@ -0,0 +1,320 @@
<?php
use Opcodes\LogViewer\Enums\FolderSortingMethod;
use Opcodes\LogViewer\Enums\SortingOrder;
use Opcodes\LogViewer\Enums\Theme;
return [
/*
|--------------------------------------------------------------------------
| Log Viewer
|--------------------------------------------------------------------------
| Log Viewer can be disabled, so it's no longer accessible via browser.
|
*/
'enabled' => env('LOG_VIEWER_ENABLED', true),
'api_only' => env('LOG_VIEWER_API_ONLY', false),
'require_auth_in_production' => true,
/*
|--------------------------------------------------------------------------
| Log Viewer Domain
|--------------------------------------------------------------------------
| You may change the domain where Log Viewer should be active.
| If the domain is empty, all domains will be valid.
|
*/
'route_domain' => null,
/*
|--------------------------------------------------------------------------
| Log Viewer Route
|--------------------------------------------------------------------------
| Log Viewer will be available under this URL.
|
*/
'route_path' => 'log-viewer',
/*
|--------------------------------------------------------------------------
| Back to system URL
|--------------------------------------------------------------------------
| When set, displays a link to easily get back to this URL.
| Set to `null` to hide this link.
|
| Optional label to display for the above URL.
|
*/
'back_to_system_url' => config('app.url', null),
'back_to_system_label' => null, // Displayed by default: "Back to {{ app.name }}"
/*
|--------------------------------------------------------------------------
| Log Viewer time zone.
|--------------------------------------------------------------------------
| The time zone in which to display the times in the UI. Defaults to
| the application's timezone defined in config/app.php.
|
*/
'timezone' => null,
/*
|--------------------------------------------------------------------------
| Log Viewer datetime format.
|--------------------------------------------------------------------------
| The format used to display timestamps in the UI.
|
*/
'datetime_format' => 'Y-m-d H:i:s',
/*
|--------------------------------------------------------------------------
| Log Viewer route middleware.
|--------------------------------------------------------------------------
| Optional middleware to use when loading the initial Log Viewer page.
|
*/
'middleware' => [
'web',
'auth',
\Opcodes\LogViewer\Http\Middleware\AuthorizeLogViewer::class,
],
/*
|--------------------------------------------------------------------------
| Log Viewer API middleware.
|--------------------------------------------------------------------------
| Optional middleware to use on every API request. The same API is also
| used from within the Log Viewer user interface.
|
*/
'api_middleware' => [
\Opcodes\LogViewer\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Opcodes\LogViewer\Http\Middleware\AuthorizeLogViewer::class,
],
'api_stateful_domains' => env('LOG_VIEWER_API_STATEFUL_DOMAINS') ? explode(',', env('LOG_VIEWER_API_STATEFUL_DOMAINS')) : null,
/*
|--------------------------------------------------------------------------
| Log Viewer Remote hosts.
|--------------------------------------------------------------------------
| Log Viewer supports viewing Laravel logs from remote hosts. They must
| be running Log Viewer as well. Below you can define the hosts you
| would like to show in this Log Viewer instance.
|
*/
'hosts' => [
'local' => [
'name' => ucfirst(env('APP_ENV', 'local')),
],
// 'staging' => [
// 'name' => 'Staging',
// 'host' => 'https://staging.example.com/log-viewer',
// 'auth' => [ // Example of HTTP Basic auth
// 'username' => 'username',
// 'password' => 'password',
// ],
// 'verify_server_certificate' => true,
// ],
//
// 'production' => [
// 'name' => 'Production',
// 'host' => 'https://example.com/log-viewer',
// 'auth' => [ // Example of Bearer token auth
// 'token' => env('LOG_VIEWER_PRODUCTION_TOKEN'),
// ],
// 'headers' => [
// 'X-Foo' => 'Bar',
// ],
// 'verify_server_certificate' => true,
// ],
],
/*
|--------------------------------------------------------------------------
| Include file patterns
|--------------------------------------------------------------------------
|
*/
'include_files' => [
'*.log',
'**/*.log',
// You can include paths to other log types as well, such as apache, nginx, and more.
// This key => value pair can be used to rename and group multiple paths into one folder in the UI.
'/var/log/httpd/*' => 'Apache',
'/var/log/nginx/*' => 'Nginx',
// MacOS Apple Silicon logs
'/opt/homebrew/var/log/nginx/*',
'/opt/homebrew/var/log/httpd/*',
'/opt/homebrew/var/log/php-fpm.log',
'/opt/homebrew/var/log/postgres*log',
'/opt/homebrew/var/log/redis*log',
'/opt/homebrew/var/log/supervisor*log',
// '/absolute/paths/supported',
],
/*
|--------------------------------------------------------------------------
| Exclude file patterns.
|--------------------------------------------------------------------------
| This will take precedence over included files.
|
*/
'exclude_files' => [
// 'my_secret.log'
],
/*
|--------------------------------------------------------------------------
| Hide unknown files.
|--------------------------------------------------------------------------
| The include/exclude options above might catch files which are not
| logs supported by Log Viewer. In that case, you can hide them
| from the UI and API calls by setting this to true.
|
*/
'hide_unknown_files' => true,
/*
|--------------------------------------------------------------------------
| Shorter stack trace filters.
|--------------------------------------------------------------------------
| Lines containing any of these strings will be excluded from the full log.
| This setting is only active when the function is enabled via the user interface.
|
*/
'shorter_stack_trace_excludes' => [
'/vendor/symfony/',
'/vendor/laravel/framework/',
'/vendor/barryvdh/laravel-debugbar/',
],
/*
|--------------------------------------------------------------------------
| Cache driver
|--------------------------------------------------------------------------
| Cache driver to use for storing the log indices. Indices are used to speed up
| log navigation. Defaults to your application's default cache driver.
|
*/
'cache_driver' => env('LOG_VIEWER_CACHE_DRIVER', null),
/*
|--------------------------------------------------------------------------
| Cache key prefix
|--------------------------------------------------------------------------
| Log Viewer prefixes all the cache keys created with this value. If for
| some reason you would like to change this prefix, you can do so here.
| The format of Log Viewer cache keys is:
| {prefix}:{version}:{rest-of-the-key}
|
*/
'cache_key_prefix' => 'lv',
/*
|--------------------------------------------------------------------------
| Chunk size when scanning log files lazily
|--------------------------------------------------------------------------
| The size in MB of files to scan before updating the progress bar when searching across all files.
|
*/
'lazy_scan_chunk_size_in_mb' => 50,
'strip_extracted_context' => true,
/*
|--------------------------------------------------------------------------
| Per page options
|--------------------------------------------------------------------------
| Define the available options for number of results per page
|
*/
'per_page_options' => [10, 25, 50, 100, 250, 500],
/*
|--------------------------------------------------------------------------
| Default settings for Log Viewer
|--------------------------------------------------------------------------
| These settings determine the default behaviour of Log Viewer. Many of
| these can be persisted for the user in their browser's localStorage,
| if the `use_local_storage` option is set to true.
|
*/
'defaults' => [
// Whether to use browser's localStorage to store user preferences.
// If true, user preferences saved in the browser will take precedence over the defaults below.
'use_local_storage' => true,
// Method to sort the folders. Other options: `Alphabetical`, `ModifiedTime`
'folder_sorting_method' => FolderSortingMethod::ModifiedTime,
// Order to sort the folders. Other options: `Ascending`, `Descending`
'folder_sorting_order' => SortingOrder::Descending,
// Order to sort the logs. Other options: `Ascending`, `Descending`
'log_sorting_order' => SortingOrder::Descending,
// Number of results per page. Must be one of the above `per_page_options` values
'per_page' => 25,
// Color scheme for the Log Viewer. Other options: `System`, `Light`, `Dark`
'theme' => Theme::System,
// Whether to enable `Shorter Stack Traces` option by default
'shorter_stack_traces' => false,
],
/*
|--------------------------------------------------------------------------
| Exclude IP from identifiers
|--------------------------------------------------------------------------
| By default, file and folder identifiers include the server's IP address
| to ensure uniqueness. In load-balanced environments with shared storage,
| this can cause "No results" errors. Set to true to exclude IP addresses
| from identifier generation for consistent results across servers.
|
*/
'exclude_ip_from_identifiers' => env('LOG_VIEWER_EXCLUDE_IP_FROM_IDENTIFIERS', false),
/*
|--------------------------------------------------------------------------
| Root folder prefix
|--------------------------------------------------------------------------
| The prefix for log files inside Laravel's `storage/logs` folder.
| Log Viewer does not show the full path to these files in the UI,
| but only the filename prefixed with this value.
|
*/
'root_folder_prefix' => 'root',
];

138
config/logging.php Normal file
View File

@@ -0,0 +1,138 @@
<?php
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
return [
/*
|--------------------------------------------------------------------------
| Default Log Channel
|--------------------------------------------------------------------------
|
| This option defines the default log channel that gets used when writing
| messages to the logs. The name specified in this option should match
| one of the channels defined in the "channels" configuration array.
|
*/
'default' => env('LOG_CHANNEL', 'stack'),
/*
|--------------------------------------------------------------------------
| Deprecations Log Channel
|--------------------------------------------------------------------------
|
| This option controls the log channel that should be used to log warnings
| regarding deprecated PHP and library features. This allows you to get
| your application ready for upcoming major versions of dependencies.
|
*/
'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
/*
|--------------------------------------------------------------------------
| Log Channels
|--------------------------------------------------------------------------
|
| Here you may configure the log channels for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Drivers: "single", "daily", "slack", "syslog",
| "errorlog", "monolog",
| "custom", "stack"
|
*/
'channels' => [
'stack' => [
'driver' => 'stack',
// 'channels' => ['daily', 'mattermost', 'mattermost_log'],
'channels' => ['daily'],
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => 14,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'Laravel Log',
'emoji' => ':boom:',
'level' => env('LOG_LEVEL', 'critical'),
],
'papertrail' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => SyslogUdpHandler::class,
'handler_with' => [
'host' => env('PAPERTRAIL_URL'),
'port' => env('PAPERTRAIL_PORT'),
],
],
'stderr' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => StreamHandler::class,
'formatter' => env('LOG_STDERR_FORMATTER'),
'with' => [
'stream' => 'php://stderr',
],
],
'syslog' => [
'driver' => 'syslog',
'level' => env('LOG_LEVEL', 'debug'),
],
'errorlog' => [
'driver' => 'errorlog',
'level' => env('LOG_LEVEL', 'debug'),
],
'null' => [
'driver' => 'monolog',
'handler' => NullHandler::class,
],
'emergency' => [
'path' => storage_path('logs/laravel.log'),
],
/*
'mattermost' => [
'driver' => 'custom',
'via' => ThibaudDauce\MattermostLogger\MattermostLogger::class,
'webhook' => env('MATTERMOST_WEBHOOK'),
'level' => env('LOG_LEVEL', 'debug'),
'channel' => env('MATTERMOST_CHANNEL', 'click-airtime-ussd-portal'),
],
'mattermost_log' => [
'driver' => 'custom',
'via' => App\Logging\CustomLogger::class,
'url' => env('MATTERMOST_LOG_URL'),
'level' => 'error',
],
*/
// 'mycustom' => [
// 'driver' => 'custom',
// 'via' => \App\Logging\CustomLogger::class
// ],
],
];

Some files were not shown because too many files have changed in this diff Show More