mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-03 08:54:34 -04:00
Merge remote-tracking branch 'upstream/v5-develop' into v5-client-portal-tests-dusk
This commit is contained in:
commit
2be806bb8e
29
README.md
29
README.md
@ -4,14 +4,19 @@
|
|||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
[](https://www.codacy.com/gh/turbo124/invoiceninja/dashboard?utm_source=github.com&utm_medium=referral&utm_content=turbo124/invoiceninja&utm_campaign=Badge_Grade)
|
||||||
[](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
|
|
||||||
|
|
||||||
# Invoice Ninja version 5!
|
# Invoice Ninja version 5!
|
||||||
|
|
||||||
## Quick Start
|
## Preamble
|
||||||
|
|
||||||
Currently the client portal and API are of alpha quality, to get started:
|
Version 5 of Invoice Ninja is here! We've taken the best parts of version 4 and bolted on all of the most requested features to produce a invoicing application like no other.
|
||||||
|
|
||||||
|
The new interface has a lot more functionality so it isn't a carbon copy of v4, but once you get used to the new layout and functionality we are sure you will love it!
|
||||||
|
|
||||||
|
If you have any questions, please join us on our [forum](https://forum.invoiceninja.com) or on [slack](https://invoiceninja.slack.com)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/invoiceninja/invoiceninja.git
|
git clone https://github.com/invoiceninja/invoiceninja.git
|
||||||
@ -69,6 +74,8 @@ To improve chances of PRs being merged please include tests to ensure your code
|
|||||||
|
|
||||||
API documentation is hosted using Swagger and can be found [HERE](https://app.swaggerhub.com/apis/invoiceninja/invoiceninja)
|
API documentation is hosted using Swagger and can be found [HERE](https://app.swaggerhub.com/apis/invoiceninja/invoiceninja)
|
||||||
|
|
||||||
|
Installation, Configuration and Troubleshooting documentation can be found [HERE] (https://invoiceninja.github.io)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
* [Hillel Coren](https://hillelcoren.com/)
|
* [Hillel Coren](https://hillelcoren.com/)
|
||||||
* [David Bomba](https://github.com/turbo124)
|
* [David Bomba](https://github.com/turbo124)
|
||||||
@ -85,10 +92,18 @@ API documentation is hosted using Swagger and can be found [HERE](https://app.sw
|
|||||||
|
|
||||||
## Current work in progress
|
## Current work in progress
|
||||||
|
|
||||||
Invoice Ninja is currently being written in a combination of Laravel for the API and Client Portal and Flutter for the front end management console. This will allow an immersive and consistent experience across any device: mobile, tablet or desktop.
|
Invoice Ninja is written in a combination of technologies:
|
||||||
|
|
||||||
To manage our workflow we will be creating separate branches for the client (Flutter) and server (Laravel API / Client Portal) and merge these into a release branch for deployments.
|
API - Laravel
|
||||||
|
Client Portal - Laravel + Tailwind
|
||||||
|
Admin Portal - Flutter
|
||||||
|
|
||||||
|
This allows an immersive and consistent experience across any device: mobile, tablet or desktop.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
If you find a security issue with this application please send an email to contact@invoiceninja.com Please follow responsible disclosure procedures if you detect an issue. For further information on responsible disclosure please read [here](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Invoice Ninja is released under the Attribution Assurance License.
|
Invoice Ninja is released under the Elastic License.
|
||||||
See [LICENSE](LICENSE) for details.
|
See [LICENSE](LICENSE) for details.
|
||||||
|
@ -1 +1 @@
|
|||||||
5.2.7
|
5.2.11
|
@ -234,38 +234,38 @@ class CheckData extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for more than one primary contact
|
// // check for more than one primary contact
|
||||||
$clients = DB::table('clients')
|
// $clients = DB::table('clients')
|
||||||
->leftJoin('client_contacts', function ($join) {
|
// ->leftJoin('client_contacts', function ($join) {
|
||||||
$join->on('client_contacts.client_id', '=', 'clients.id')
|
// $join->on('client_contacts.client_id', '=', 'clients.id')
|
||||||
->where('client_contacts.is_primary', '=', true)
|
// ->where('client_contacts.is_primary', '=', true)
|
||||||
->whereNull('client_contacts.deleted_at');
|
// ->whereNull('client_contacts.deleted_at');
|
||||||
})
|
// })
|
||||||
->groupBy('clients.id')
|
// ->groupBy('clients.id')
|
||||||
->havingRaw('count(client_contacts.id) != 1');
|
// ->havingRaw('count(client_contacts.id) != 1');
|
||||||
|
|
||||||
if ($this->option('client_id')) {
|
// if ($this->option('client_id')) {
|
||||||
$clients->where('clients.id', '=', $this->option('client_id'));
|
// $clients->where('clients.id', '=', $this->option('client_id'));
|
||||||
}
|
// }
|
||||||
|
|
||||||
$clients = $clients->get(['clients.id', 'clients.user_id', 'clients.company_id']);
|
// $clients = $clients->get(['clients.id', 'clients.user_id', 'clients.company_id']);
|
||||||
$this->logMessage($clients->count().' clients without a single primary contact');
|
// // $this->logMessage($clients->count().' clients without a single primary contact');
|
||||||
|
|
||||||
if ($this->option('fix') == 'true') {
|
// // if ($this->option('fix') == 'true') {
|
||||||
foreach ($clients as $client) {
|
// // foreach ($clients as $client) {
|
||||||
$this->logMessage("Fixing missing primary contacts #{$client->id}");
|
// // $this->logMessage("Fixing missing primary contacts #{$client->id}");
|
||||||
|
|
||||||
$new_contact = ClientContactFactory::create($client->company_id, $client->user_id);
|
// // $new_contact = ClientContactFactory::create($client->company_id, $client->user_id);
|
||||||
$new_contact->client_id = $client->id;
|
// // $new_contact->client_id = $client->id;
|
||||||
$new_contact->contact_key = Str::random(40);
|
// // $new_contact->contact_key = Str::random(40);
|
||||||
$new_contact->is_primary = true;
|
// // $new_contact->is_primary = true;
|
||||||
$new_contact->save();
|
// // $new_contact->save();
|
||||||
}
|
// // }
|
||||||
}
|
// // }
|
||||||
|
|
||||||
if ($clients->count() > 0) {
|
// if ($clients->count() > 0) {
|
||||||
$this->isValid = false;
|
// $this->isValid = false;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkFailedJobs()
|
private function checkFailedJobs()
|
||||||
@ -365,7 +365,7 @@ class CheckData extends Command
|
|||||||
/* Due to accounting differences we need to perform a second loop here to ensure there actually is an issue */
|
/* Due to accounting differences we need to perform a second loop here to ensure there actually is an issue */
|
||||||
$clients->each(function ($client_record) use ($credit_total_applied) {
|
$clients->each(function ($client_record) use ($credit_total_applied) {
|
||||||
|
|
||||||
$client = Client::find($client_record->id);
|
$client = Client::withTrashed()->find($client_record->id);
|
||||||
|
|
||||||
$total_invoice_payments = 0;
|
$total_invoice_payments = 0;
|
||||||
|
|
||||||
@ -594,6 +594,7 @@ class CheckData extends Command
|
|||||||
'client',
|
'client',
|
||||||
'client_contact',
|
'client_contact',
|
||||||
'payment',
|
'payment',
|
||||||
|
'recurring_invoice',
|
||||||
],
|
],
|
||||||
'invoices' => [
|
'invoices' => [
|
||||||
'client',
|
'client',
|
||||||
|
@ -64,7 +64,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
$schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping();
|
$schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping();
|
||||||
|
|
||||||
$schedule->job(new SchedulerCheck)->everyFiveMinutes();
|
$schedule->job(new SchedulerCheck)->daily()->withoutOverlapping();
|
||||||
|
|
||||||
/* Run hosted specific jobs */
|
/* Run hosted specific jobs */
|
||||||
if (Ninja::isHosted()) {
|
if (Ninja::isHosted()) {
|
||||||
|
@ -243,7 +243,7 @@ class CompanySettings extends BaseSettings
|
|||||||
public $font_size = 7; //@implemented
|
public $font_size = 7; //@implemented
|
||||||
public $primary_font = 'Roboto';
|
public $primary_font = 'Roboto';
|
||||||
public $secondary_font = 'Roboto';
|
public $secondary_font = 'Roboto';
|
||||||
public $primary_color = '#142cb5';
|
public $primary_color = '#298AAB';
|
||||||
public $secondary_color = '#7081e0';
|
public $secondary_color = '#7081e0';
|
||||||
|
|
||||||
public $hide_paid_to_date = false; //@TODO where?
|
public $hide_paid_to_date = false; //@TODO where?
|
||||||
|
@ -19,6 +19,8 @@ class InvoiceItem
|
|||||||
|
|
||||||
public $product_key = '';
|
public $product_key = '';
|
||||||
|
|
||||||
|
public $product_cost = 0;
|
||||||
|
|
||||||
public $notes = '';
|
public $notes = '';
|
||||||
|
|
||||||
public $discount = 0;
|
public $discount = 0;
|
||||||
@ -57,6 +59,7 @@ class InvoiceItem
|
|||||||
'type_id' => 'string',
|
'type_id' => 'string',
|
||||||
'quantity' => 'float',
|
'quantity' => 'float',
|
||||||
'cost' => 'float',
|
'cost' => 'float',
|
||||||
|
'product_cost' => 'float',
|
||||||
'product_key' => 'string',
|
'product_key' => 'string',
|
||||||
'notes' => 'string',
|
'notes' => 'string',
|
||||||
'discount' => 'float',
|
'discount' => 'float',
|
||||||
|
@ -81,17 +81,23 @@ class Handler extends ExceptionHandler
|
|||||||
|
|
||||||
app('sentry')->configureScope(function (Scope $scope): void {
|
app('sentry')->configureScope(function (Scope $scope): void {
|
||||||
|
|
||||||
if(auth()->guard('contact') && auth()->guard('contact')->user())
|
$name = 'hosted@invoiceninja.com';
|
||||||
|
|
||||||
|
if(auth()->guard('contact') && auth()->guard('contact')->user()){
|
||||||
|
$name = "Contact = ".auth()->guard('contact')->user()->email;
|
||||||
$key = auth()->guard('contact')->user()->company->account->key;
|
$key = auth()->guard('contact')->user()->company->account->key;
|
||||||
elseif (auth()->guard('user') && auth()->guard('user')->user())
|
}
|
||||||
|
elseif (auth()->guard('user') && auth()->guard('user')->user()){
|
||||||
|
$name = "Admin = ".auth()->guard('user')->user()->email;
|
||||||
$key = auth()->user()->account->key;
|
$key = auth()->user()->account->key;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
$key = 'Anonymous';
|
$key = 'Anonymous';
|
||||||
|
|
||||||
$scope->setUser([
|
$scope->setUser([
|
||||||
'id' => 'Hosted_User',
|
'id' => $key,
|
||||||
'email' => 'hosted@invoiceninja.com',
|
'email' => 'hosted@invoiceninja.com',
|
||||||
'name' => $key,
|
'name' => $name,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -120,8 +126,7 @@ class Handler extends ExceptionHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if(config('ninja.expanded_logging'))
|
parent::report($exception);
|
||||||
parent::report($exception);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +196,7 @@ class Handler extends ExceptionHandler
|
|||||||
} elseif ($exception instanceof GenericPaymentDriverFailure && $request->expectsJson()) {
|
} elseif ($exception instanceof GenericPaymentDriverFailure && $request->expectsJson()) {
|
||||||
return response()->json(['message' => $exception->getMessage()], 400);
|
return response()->json(['message' => $exception->getMessage()], 400);
|
||||||
} elseif ($exception instanceof GenericPaymentDriverFailure) {
|
} elseif ($exception instanceof GenericPaymentDriverFailure) {
|
||||||
$data['message'] = $exception->getMessage();
|
return response()->json(['message' => $exception->getMessage()], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::render($request, $exception);
|
return parent::render($request, $exception);
|
||||||
|
@ -22,7 +22,7 @@ class ExpenseCategoryFactory
|
|||||||
$expense->company_id = $company_id;
|
$expense->company_id = $company_id;
|
||||||
$expense->name = '';
|
$expense->name = '';
|
||||||
$expense->is_deleted = false;
|
$expense->is_deleted = false;
|
||||||
$expense->color = '#fff';
|
$expense->color = '';
|
||||||
|
|
||||||
return $expense;
|
return $expense;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ class TaskStatusFactory
|
|||||||
$task_status->user_id = $user_id;
|
$task_status->user_id = $user_id;
|
||||||
$task_status->company_id = $company_id;
|
$task_status->company_id = $company_id;
|
||||||
$task_status->name = '';
|
$task_status->name = '';
|
||||||
$task_status->color = '#fff';
|
$task_status->color = '';
|
||||||
$task_status->status_order = 9999;
|
$task_status->status_order = 9999;
|
||||||
|
|
||||||
return $task_status;
|
return $task_status;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\ClientPortal\Contact\ContactPasswordResetRequest;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
@ -73,9 +74,8 @@ class ContactForgotPasswordController extends Controller
|
|||||||
return Password::broker('contacts');
|
return Password::broker('contacts');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendResetLinkEmail(Request $request)
|
public function sendResetLinkEmail(ContactPasswordResetRequest $request)
|
||||||
{
|
{
|
||||||
//MultiDB::userFindAndSetDb($request->input('email'));
|
|
||||||
|
|
||||||
$user = MultiDB::hasContact($request->input('email'));
|
$user = MultiDB::hasContact($request->input('email'));
|
||||||
|
|
||||||
|
@ -35,10 +35,16 @@ class ContactLoginController extends Controller
|
|||||||
|
|
||||||
public function showLoginForm(Request $request)
|
public function showLoginForm(Request $request)
|
||||||
{
|
{
|
||||||
if (strpos($request->getHost(), 'invoicing.co') !== false) {
|
//if we are on the root domain invoicing.co do not show any company logos
|
||||||
|
if(Ninja::isHosted() && count(explode('.', request()->getHost())) == 2){
|
||||||
|
$company = null;
|
||||||
|
}elseif (strpos($request->getHost(), 'invoicing.co') !== false) {
|
||||||
$subdomain = explode('.', $request->getHost())[0];
|
$subdomain = explode('.', $request->getHost())[0];
|
||||||
$company = Company::where('subdomain', $subdomain)->first();
|
$company = Company::where('subdomain', $subdomain)->first();
|
||||||
} elseif (Ninja::isSelfHost()) {
|
} elseif(Ninja::isHosted() && $company = Company::where('portal_domain', $request->getSchemeAndHttpHost())->first()){
|
||||||
|
|
||||||
|
}
|
||||||
|
elseif (Ninja::isSelfHost()) {
|
||||||
$company = Account::first()->default_company;
|
$company = Account::first()->default_company;
|
||||||
} else {
|
} else {
|
||||||
$company = null;
|
$company = null;
|
||||||
|
@ -488,6 +488,8 @@ class LoginController extends BaseController
|
|||||||
auth()->user()->email_verified_at = now();
|
auth()->user()->email_verified_at = now();
|
||||||
auth()->user()->save();
|
auth()->user()->save();
|
||||||
|
|
||||||
|
auth()->user()->setCompany(auth()->user()->account->default_company);
|
||||||
|
|
||||||
$this->setLoginCache(auth()->user());
|
$this->setLoginCache(auth()->user());
|
||||||
|
|
||||||
$cu = CompanyUser::whereUserId(auth()->user()->id);
|
$cu = CompanyUser::whereUserId(auth()->user()->id);
|
||||||
|
@ -24,6 +24,7 @@ use Illuminate\Contracts\View\Factory;
|
|||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use ZipStream\Option\Archive;
|
use ZipStream\Option\Archive;
|
||||||
use ZipStream\ZipStream;
|
use ZipStream\ZipStream;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class InvoiceController extends Controller
|
class InvoiceController extends Controller
|
||||||
{
|
{
|
||||||
@ -170,8 +171,10 @@ class InvoiceController extends Controller
|
|||||||
$invitation = $invoice->invitations->first();
|
$invitation = $invoice->invitations->first();
|
||||||
//$file = $invoice->pdf_file_path($invitation);
|
//$file = $invoice->pdf_file_path($invitation);
|
||||||
$file = $invoice->service()->getInvoicePdf(auth()->user());
|
$file = $invoice->service()->getInvoicePdf(auth()->user());
|
||||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
||||||
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable output of HTTP headers
|
// enable output of HTTP headers
|
||||||
|
@ -27,6 +27,7 @@ use Illuminate\View\View;
|
|||||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||||
use ZipStream\Option\Archive;
|
use ZipStream\Option\Archive;
|
||||||
use ZipStream\ZipStream;
|
use ZipStream\ZipStream;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class QuoteController extends Controller
|
class QuoteController extends Controller
|
||||||
{
|
{
|
||||||
@ -89,8 +90,11 @@ class QuoteController extends Controller
|
|||||||
|
|
||||||
if ($quotes->count() == 1) {
|
if ($quotes->count() == 1) {
|
||||||
|
|
||||||
$file = $quotes->first()->pdf_file_path();
|
$file = $quotes->first()->service()->getQuotePdf();
|
||||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable output of HTTP headers
|
// enable output of HTTP headers
|
||||||
|
@ -474,6 +474,10 @@ class CompanyController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function destroy(DestroyCompanyRequest $request, Company $company)
|
public function destroy(DestroyCompanyRequest $request, Company $company)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if(Ninja::isHosted() && config('ninja.ninja_default_company_id') == $company->id)
|
||||||
|
return response()->json(['message' => 'Cannot purge this company'], 400);
|
||||||
|
|
||||||
$company_count = $company->account->companies->count();
|
$company_count = $company->account->companies->count();
|
||||||
$account = $company->account;
|
$account = $company->account;
|
||||||
$account_key = $account->key;
|
$account_key = $account->key;
|
||||||
|
@ -37,6 +37,7 @@ use App\Utils\TempFile;
|
|||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use App\Utils\Traits\SavesDocuments;
|
use App\Utils\Traits\SavesDocuments;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class CreditController.
|
* Class CreditController.
|
||||||
@ -536,8 +537,14 @@ class CreditController extends BaseController
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'download':
|
case 'download':
|
||||||
$file = $credit->pdf_file_path();
|
// $file = $credit->pdf_file_path();
|
||||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
$file = $credit->service()->getCreditPdf($credit->invitations->first());
|
||||||
|
|
||||||
|
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
break;
|
break;
|
||||||
case 'archive':
|
case 'archive':
|
||||||
$this->credit_repository->archive($credit);
|
$this->credit_repository->archive($credit);
|
||||||
@ -585,9 +592,12 @@ class CreditController extends BaseController
|
|||||||
// $contact = $invitation->contact;
|
// $contact = $invitation->contact;
|
||||||
$credit = $invitation->credit;
|
$credit = $invitation->credit;
|
||||||
|
|
||||||
$file_path = $credit->service()->getCreditPdf($invitation);
|
$file = $credit->service()->getCreditPdf($invitation);
|
||||||
|
|
||||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
|
// return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -672,8 +672,17 @@ class InvoiceController extends BaseController
|
|||||||
break;
|
break;
|
||||||
case 'download':
|
case 'download':
|
||||||
|
|
||||||
$file = $invoice->pdf_file_path();
|
// $file = $invoice->pdf_file_path();
|
||||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
|
|
||||||
|
$file = $invoice->service()->getInvoicePdf();
|
||||||
|
|
||||||
|
// return response()->download(Storage::get($file), basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'restore':
|
case 'restore':
|
||||||
@ -722,10 +731,11 @@ class InvoiceController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
//touch reminder1,2,3_sent + last_sent here if the email is a reminder.
|
//touch reminder1,2,3_sent + last_sent here if the email is a reminder.
|
||||||
$invoice->service()->touchReminder($this->reminder_template)->deletePdf()->save();
|
//$invoice->service()->touchReminder($this->reminder_template)->deletePdf()->save();
|
||||||
|
$invoice->service()->touchReminder($this->reminder_template)->markSent()->save();
|
||||||
|
|
||||||
$invoice->invitations->load('contact.client.country', 'invoice.client.country', 'invoice.company')->each(function ($invitation) use ($invoice) {
|
$invoice->invitations->load('contact.client.country', 'invoice.client.country', 'invoice.company')->each(function ($invitation) use ($invoice) {
|
||||||
EmailEntity::dispatch($invitation, $invoice->company, $this->reminder_template);
|
EmailEntity::dispatch($invitation, $invoice->company, $this->reminder_template)->delay(now()->addSeconds(30));
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($invoice->invitations->count() >= 1) {
|
if ($invoice->invitations->count() >= 1) {
|
||||||
@ -795,8 +805,11 @@ class InvoiceController extends BaseController
|
|||||||
|
|
||||||
$file = $invoice->service()->getInvoicePdf($contact);
|
$file = $invoice->service()->getInvoicePdf($contact);
|
||||||
|
|
||||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
// return response()->download(Storage::get($file), basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -848,7 +861,10 @@ class InvoiceController extends BaseController
|
|||||||
|
|
||||||
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||||
|
|
||||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
// return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ use App\Utils\CurlUtils;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
class LicenseController extends BaseController
|
class LicenseController extends BaseController
|
||||||
{
|
{
|
||||||
@ -152,7 +153,7 @@ class LicenseController extends BaseController
|
|||||||
{
|
{
|
||||||
$account = auth()->user()->company()->account;
|
$account = auth()->user()->company()->account;
|
||||||
|
|
||||||
if($account->plan == 'white_label' && $account->plan_expires->lt(now())){
|
if($account->plan == 'white_label' && Carbon::parse($account->plan_expires)->lt(now())){
|
||||||
$account->plan = null;
|
$account->plan = null;
|
||||||
$account->plan_expires = null;
|
$account->plan_expires = null;
|
||||||
$account->save();
|
$account->save();
|
||||||
|
@ -82,6 +82,9 @@ class MigrationController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function purgeCompany(Company $company)
|
public function purgeCompany(Company $company)
|
||||||
{
|
{
|
||||||
|
if(Ninja::isHosted() && config('ninja.ninja_default_company_id') == $company->id)
|
||||||
|
return response()->json(['message' => 'Cannot purge this company'], 400);
|
||||||
|
|
||||||
$account = $company->account;
|
$account = $company->account;
|
||||||
$company_id = $company->id;
|
$company_id = $company->id;
|
||||||
|
|
||||||
@ -102,6 +105,9 @@ class MigrationController extends BaseController
|
|||||||
|
|
||||||
private function purgeCompanyWithForceFlag(Company $company)
|
private function purgeCompanyWithForceFlag(Company $company)
|
||||||
{
|
{
|
||||||
|
if(Ninja::isHosted() && config('ninja.ninja_default_company_id') == $company->id)
|
||||||
|
return response()->json(['message' => 'Cannot purge this company'], 400);
|
||||||
|
|
||||||
$account = $company->account;
|
$account = $company->account;
|
||||||
$company_id = $company->id;
|
$company_id = $company->id;
|
||||||
|
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\Payments\PaymentNotificationWebhookRequest;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\CompanyGateway;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Auth;
|
||||||
|
|
||||||
|
class PaymentNotificationWebhookController extends Controller
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
public function __invoke(PaymentNotificationWebhookRequest $request, string $company_key, string $company_gateway_id, string $client_hash)
|
||||||
|
{
|
||||||
|
|
||||||
|
$company_gateway = CompanyGateway::find($this->decodePrimaryKey($company_gateway_id));
|
||||||
|
$client = Client::find($this->decodePrimaryKey($client_hash));
|
||||||
|
|
||||||
|
return $company_gateway
|
||||||
|
->driver($client)
|
||||||
|
->processWebhookRequest($request);
|
||||||
|
}
|
||||||
|
}
|
@ -39,6 +39,7 @@ use App\Utils\Traits\MakesHash;
|
|||||||
use App\Utils\Traits\SavesDocuments;
|
use App\Utils\Traits\SavesDocuments;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class QuoteController.
|
* Class QuoteController.
|
||||||
@ -676,8 +677,14 @@ class QuoteController extends BaseController
|
|||||||
break;
|
break;
|
||||||
case 'download':
|
case 'download':
|
||||||
|
|
||||||
$file = $quote->pdf_file_path();
|
//$file = $quote->pdf_file_path();
|
||||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
$file = $quote->service()->getQuotePdf();
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
|
|
||||||
|
//return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'restore':
|
case 'restore':
|
||||||
@ -728,9 +735,14 @@ class QuoteController extends BaseController
|
|||||||
$contact = $invitation->contact;
|
$contact = $invitation->contact;
|
||||||
$quote = $invitation->quote;
|
$quote = $invitation->quote;
|
||||||
|
|
||||||
$file_path = $quote->service()->getQuotePdf($contact);
|
$file = $quote->service()->getQuotePdf($contact);
|
||||||
|
nlog($file);
|
||||||
|
|
||||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
|
|
||||||
|
// return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,6 +33,7 @@ use App\Utils\Traits\SavesDocuments;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class RecurringInvoiceController.
|
* Class RecurringInvoiceController.
|
||||||
@ -500,9 +501,12 @@ class RecurringInvoiceController extends BaseController
|
|||||||
$contact = $invitation->contact;
|
$contact = $invitation->contact;
|
||||||
$recurring_invoice = $invitation->recurring_invoice;
|
$recurring_invoice = $invitation->recurring_invoice;
|
||||||
|
|
||||||
$file_path = $recurring_invoice->service()->getInvoicePdf($contact);
|
$file = $recurring_invoice->service()->getInvoicePdf($contact);
|
||||||
|
|
||||||
|
return response()->streamDownload(function () use($file) {
|
||||||
|
echo Storage::get($file);
|
||||||
|
}, basename($file));
|
||||||
|
|
||||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -241,6 +241,11 @@ class SetupController extends Controller
|
|||||||
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
|
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config('ninja.snappdf_chromium_arguments')) {
|
||||||
|
$pdf->clearChromiumArguments();
|
||||||
|
$pdf->addChromiumArguments(config('ninja.snappdf_chromium_arguments'));
|
||||||
|
}
|
||||||
|
|
||||||
$pdf = $pdf
|
$pdf = $pdf
|
||||||
->setHtml('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!')
|
->setHtml('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!')
|
||||||
->generate();
|
->generate();
|
||||||
|
@ -60,12 +60,6 @@ class StripeConnectController extends BaseController
|
|||||||
$redirect_uri = 'https://invoicing.co/stripe/completed';
|
$redirect_uri = 'https://invoicing.co/stripe/completed';
|
||||||
$endpoint = "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={$stripe_client_id}&redirect_uri={$redirect_uri}&scope=read_write&state={$token}";
|
$endpoint = "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={$stripe_client_id}&redirect_uri={$redirect_uri}&scope=read_write&state={$token}";
|
||||||
|
|
||||||
// if($email = $request->getContact()->email)
|
|
||||||
// $endpoint .= "&stripe_user[email]={$email}";
|
|
||||||
|
|
||||||
// $company_name = str_replace(" ", "_", $company->present()->name());
|
|
||||||
// $endpoint .= "&stripe_user[business_name]={$company_name}";
|
|
||||||
|
|
||||||
return redirect($endpoint);
|
return redirect($endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,18 +81,24 @@ class StripeConnectController extends BaseController
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// nlog($response);
|
|
||||||
|
|
||||||
$company = Company::where('company_key', $request->getTokenContent()['company_key'])->first();
|
$company = Company::where('company_key', $request->getTokenContent()['company_key'])->first();
|
||||||
|
|
||||||
$company_gateway = CompanyGatewayFactory::create($company->id, $company->owner()->id);
|
$company_gateway = CompanyGateway::query()
|
||||||
$fees_and_limits = new \stdClass;
|
->where('gateway_key', 'd14dd26a47cecc30fdd65700bfb67b34')
|
||||||
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
->where('company_id', $company->id)
|
||||||
$company_gateway->gateway_key = 'd14dd26a47cecc30fdd65700bfb67b34';
|
->first();
|
||||||
$company_gateway->fees_and_limits = $fees_and_limits;
|
|
||||||
$company_gateway->setConfig([]);
|
if(!$company_gateway)
|
||||||
$company_gateway->token_billing = 'always';
|
{
|
||||||
// $company_gateway->save();
|
$company_gateway = CompanyGatewayFactory::create($company->id, $company->owner()->id);
|
||||||
|
$fees_and_limits = new \stdClass;
|
||||||
|
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
||||||
|
$company_gateway->gateway_key = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||||
|
$company_gateway->fees_and_limits = $fees_and_limits;
|
||||||
|
$company_gateway->setConfig([]);
|
||||||
|
$company_gateway->token_billing = 'always';
|
||||||
|
// $company_gateway->save();
|
||||||
|
}
|
||||||
|
|
||||||
$payload = [
|
$payload = [
|
||||||
'account_id' => $response->stripe_user_id,
|
'account_id' => $response->stripe_user_id,
|
||||||
@ -111,18 +111,6 @@ class StripeConnectController extends BaseController
|
|||||||
"access_token" => $response->access_token
|
"access_token" => $response->access_token
|
||||||
];
|
];
|
||||||
|
|
||||||
/* Link account if existing account exists */
|
|
||||||
// if($account_id = $this->checkAccountAlreadyLinkToEmail($company_gateway, $request->getContact()->email)) {
|
|
||||||
|
|
||||||
// $payload['account_id'] = $account_id;
|
|
||||||
// $payload['stripe_user_id'] = $account_id;
|
|
||||||
// $company_gateway->setConfig($payload);
|
|
||||||
// $company_gateway->save();
|
|
||||||
|
|
||||||
// return view('auth.connect.existing');
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
$company_gateway->setConfig($payload);
|
$company_gateway->setConfig($payload);
|
||||||
$company_gateway->save();
|
$company_gateway->save();
|
||||||
|
|
||||||
|
@ -35,6 +35,9 @@ class StripeController extends BaseController
|
|||||||
public function import()
|
public function import()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Processing'], 200);
|
||||||
|
|
||||||
|
|
||||||
if(auth()->user()->isAdmin())
|
if(auth()->user()->isAdmin())
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -34,11 +34,15 @@ class CreditsTable extends Component
|
|||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|
||||||
$query = Credit::query()
|
$query = Credit::query()
|
||||||
->where('client_id', auth('contact')->user()->client->id)
|
->where('client_id', auth('contact')->user()->client->id)
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
->where('status_id', '<>', Credit::STATUS_DRAFT)
|
->where('status_id', '<>', Credit::STATUS_DRAFT)
|
||||||
->whereDate('due_date', '<=', now())
|
->where(function ($query){
|
||||||
->orWhere('due_date', NULL)
|
$query->whereDate('due_date', '<=', now())
|
||||||
|
->orWhereNull('due_date');
|
||||||
|
})
|
||||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||||
->paginate($this->per_page);
|
->paginate($this->per_page);
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ class InvoicesTable extends Component
|
|||||||
|
|
||||||
$query = Invoice::query()
|
$query = Invoice::query()
|
||||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
->where('is_deleted', false);
|
->where('is_deleted', false);
|
||||||
|
|
||||||
if (in_array('paid', $this->status)) {
|
if (in_array('paid', $this->status)) {
|
||||||
|
@ -34,6 +34,7 @@ class PaymentMethodsTable extends Component
|
|||||||
{
|
{
|
||||||
$query = ClientGatewayToken::query()
|
$query = ClientGatewayToken::query()
|
||||||
->with('gateway_type')
|
->with('gateway_type')
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
->where('client_id', $this->client->id)
|
->where('client_id', $this->client->id)
|
||||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||||
->paginate($this->per_page);
|
->paginate($this->per_page);
|
||||||
|
@ -41,6 +41,7 @@ class PaymentsTable extends Component
|
|||||||
{
|
{
|
||||||
$query = Payment::query()
|
$query = Payment::query()
|
||||||
->with('type', 'client')
|
->with('type', 'client')
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
->where('client_id', auth('contact')->user()->client->id)
|
->where('client_id', auth('contact')->user()->client->id)
|
||||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||||
->paginate($this->per_page);
|
->paginate($this->per_page);
|
||||||
|
@ -45,6 +45,7 @@ class QuotesTable extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
$query = $query
|
$query = $query
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
->where('client_id', auth('contact')->user()->client->id)
|
->where('client_id', auth('contact')->user()->client->id)
|
||||||
->where('status_id', '<>', Quote::STATUS_DRAFT)
|
->where('status_id', '<>', Quote::STATUS_DRAFT)
|
||||||
->paginate($this->per_page);
|
->paginate($this->per_page);
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\RecurringInvoice;
|
use App\Models\RecurringInvoice;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -23,8 +24,12 @@ class RecurringInvoicesTable extends Component
|
|||||||
|
|
||||||
public $per_page = 10;
|
public $per_page = 10;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->sort_asc = false;
|
$this->sort_asc = false;
|
||||||
|
|
||||||
$this->sort_field = 'date';
|
$this->sort_field = 'date';
|
||||||
@ -36,6 +41,7 @@ class RecurringInvoicesTable extends Component
|
|||||||
|
|
||||||
$query = $query
|
$query = $query
|
||||||
->where('client_id', auth('contact')->user()->client->id)
|
->where('client_id', auth('contact')->user()->client->id)
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_PAUSED,RecurringInvoice::STATUS_COMPLETED])
|
->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_PAUSED,RecurringInvoice::STATUS_COMPLETED])
|
||||||
->orderBy('status_id', 'asc')
|
->orderBy('status_id', 'asc')
|
||||||
->with('client')
|
->with('client')
|
||||||
|
@ -36,6 +36,7 @@ class SubscriptionRecurringInvoicesTable extends Component
|
|||||||
{
|
{
|
||||||
$query = RecurringInvoice::query()
|
$query = RecurringInvoice::query()
|
||||||
->where('client_id', auth('contact')->user()->client->id)
|
->where('client_id', auth('contact')->user()->client->id)
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
->whereNotNull('subscription_id')
|
->whereNotNull('subscription_id')
|
||||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||||
->paginate($this->per_page);
|
->paginate($this->per_page);
|
||||||
|
@ -35,6 +35,7 @@ class TasksTable extends Component
|
|||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$query = Task::query()
|
$query = Task::query()
|
||||||
|
->where('company_id', $this->company->id)
|
||||||
->where('client_id', auth('contact')->user()->client->id);
|
->where('client_id', auth('contact')->user()->client->id);
|
||||||
|
|
||||||
if ($this->company->getSetting('show_all_tasks_client_portal') === 'invoiced') {
|
if ($this->company->getSetting('show_all_tasks_client_portal') === 'invoiced') {
|
||||||
|
@ -51,10 +51,10 @@ class QueryLogging
|
|||||||
$count = count($queries);
|
$count = count($queries);
|
||||||
$timeEnd = microtime(true);
|
$timeEnd = microtime(true);
|
||||||
$time = $timeEnd - $timeStart;
|
$time = $timeEnd - $timeStart;
|
||||||
|
|
||||||
//nlog($request->method().' - '.urldecode($request->url()).": $count queries - ".$time);
|
if($count > 150)
|
||||||
// if($count > 50)
|
nlog($queries);
|
||||||
//nlog($queries);
|
|
||||||
$ip = '';
|
$ip = '';
|
||||||
|
|
||||||
if(request()->header('Cf-Connecting-Ip'))
|
if(request()->header('Cf-Connecting-Ip'))
|
||||||
|
@ -30,17 +30,22 @@ class UrlSetDb
|
|||||||
*/
|
*/
|
||||||
public function handle($request, Closure $next)
|
public function handle($request, Closure $next)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (config('ninja.db.multi_db_enabled')) {
|
if (config('ninja.db.multi_db_enabled')) {
|
||||||
$hashids = new Hashids('', 10); //decoded output is _always_ an array.
|
$hashids = new Hashids(config('ninja.hash_salt'), 10);
|
||||||
|
|
||||||
//parse URL hash and set DB
|
//parse URL hash and set DB
|
||||||
$segments = explode('-', $request->route('confirmation_code'));
|
$segments = explode('-', $request->route('confirmation_code'));
|
||||||
|
|
||||||
|
if(!is_array($segments))
|
||||||
|
return response()->json(['message' => 'Invalid confirmation code'], 403);
|
||||||
|
|
||||||
$hashed_db = $hashids->decode($segments[0]);
|
$hashed_db = $hashids->decode($segments[0]);
|
||||||
|
|
||||||
MultiDB::setDB(MultiDB::DB_PREFIX.str_pad($hashed_db[0], 2, '0', STR_PAD_LEFT));
|
MultiDB::setDB(MultiDB::DB_PREFIX.str_pad($hashed_db[0], 2, '0', STR_PAD_LEFT));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests\ClientPortal\Contact;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class ContactPasswordResetRequest 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',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -67,8 +67,8 @@ class UpdateCompanyRequest extends Request
|
|||||||
{
|
{
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
// if(array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
|
if(Ninja::isHosted() && array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1)
|
||||||
// $input['portal_domain'] = str_replace("http:", "https:", $input['portal_domain']);
|
$input['portal_domain'] = $this->addScheme($input['portal_domain']);
|
||||||
|
|
||||||
if (array_key_exists('settings', $input)) {
|
if (array_key_exists('settings', $input)) {
|
||||||
$input['settings'] = $this->filterSaveableSettings($input['settings']);
|
$input['settings'] = $this->filterSaveableSettings($input['settings']);
|
||||||
@ -105,4 +105,15 @@ class UpdateCompanyRequest extends Request
|
|||||||
|
|
||||||
return $settings;
|
return $settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addScheme($url, $scheme = 'https://')
|
||||||
|
{
|
||||||
|
|
||||||
|
$url = str_replace("http://", "", $url);
|
||||||
|
|
||||||
|
$url = parse_url($url, PHP_URL_SCHEME) === null ? $scheme . $url : $url;
|
||||||
|
|
||||||
|
return rtrim($url, '/');
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ class StoreExpenseRequest extends Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||||
$input['color'] = '#fff';
|
$input['color'] = '';
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class StoreExpenseCategoryRequest extends Request
|
|||||||
$input = $this->decodePrimaryKeys($input);
|
$input = $this->decodePrimaryKeys($input);
|
||||||
|
|
||||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||||
$input['color'] = '#fff';
|
$input['color'] = '';
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ class UpdateExpenseCategoryRequest extends Request
|
|||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||||
$input['color'] = '#fff';
|
$input['color'] = '';
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,7 @@ class ImportJsonRequest extends Request
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
// 'import_type' => 'required',
|
'files' => 'file|mimes:zip',
|
||||||
// 'files' => 'required_without:hash|array|min:1|max:6',
|
|
||||||
// 'hash' => 'nullable|string',
|
|
||||||
// 'column_map' => 'required_with:hash|array',
|
|
||||||
// 'skip_header' => 'required_with:hash|boolean',
|
|
||||||
// 'files.*' => 'file|mimes:csv,txt',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Payments;
|
||||||
|
|
||||||
|
use App\Http\Requests\Request;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\CompanyGateway;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\PaymentHash;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
|
class PaymentNotificationWebhookRequest extends Request
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -51,7 +51,7 @@ class StoreProjectRequest extends Request
|
|||||||
|
|
||||||
|
|
||||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||||
$input['color'] = '#fff';
|
$input['color'] = '';
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class UpdateProjectRequest extends Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||||
$input['color'] = '#fff';
|
$input['color'] = '';
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ class UpdateTaskRequest extends Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||||
$input['color'] = '#fff';
|
$input['color'] = '';
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class StoreTaskStatusRequest extends Request
|
|||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||||
$input['color'] = '#fff';
|
$input['color'] = '';
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ class UpdateTaskStatusRequest extends Request
|
|||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||||
$input['color'] = '#fff';
|
$input['color'] = '';
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ use App\Models\Account;
|
|||||||
use App\Models\Timezone;
|
use App\Models\Timezone;
|
||||||
use App\Notifications\Ninja\NewAccountCreated;
|
use App\Notifications\Ninja\NewAccountCreated;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
|
use App\Utils\Traits\User\LoginCache;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@ -39,6 +40,7 @@ use Turbo124\Beacon\Facades\LightLogs;
|
|||||||
class CreateAccount
|
class CreateAccount
|
||||||
{
|
{
|
||||||
use Dispatchable;
|
use Dispatchable;
|
||||||
|
use LoginCache;
|
||||||
|
|
||||||
protected $request;
|
protected $request;
|
||||||
|
|
||||||
@ -77,9 +79,6 @@ class CreateAccount
|
|||||||
|
|
||||||
$sp794f3f->save();
|
$sp794f3f->save();
|
||||||
|
|
||||||
if(Ninja::isHosted())
|
|
||||||
$sp794f3f->startTrial('pro');
|
|
||||||
|
|
||||||
$sp035a66 = CreateCompany::dispatchNow($this->request, $sp794f3f);
|
$sp035a66 = CreateCompany::dispatchNow($this->request, $sp794f3f);
|
||||||
$sp035a66->load('account');
|
$sp035a66->load('account');
|
||||||
$sp794f3f->default_company_id = $sp035a66->id;
|
$sp794f3f->default_company_id = $sp035a66->id;
|
||||||
@ -95,6 +94,8 @@ class CreateAccount
|
|||||||
}
|
}
|
||||||
|
|
||||||
$spaa9f78->setCompany($sp035a66);
|
$spaa9f78->setCompany($sp035a66);
|
||||||
|
$this->setLoginCache($spaa9f78);
|
||||||
|
|
||||||
$spafe62e = isset($this->request['token_name']) ? $this->request['token_name'] : request()->server('HTTP_USER_AGENT');
|
$spafe62e = isset($this->request['token_name']) ? $this->request['token_name'] : request()->server('HTTP_USER_AGENT');
|
||||||
$sp2d97e8 = CreateCompanyToken::dispatchNow($sp035a66, $spaa9f78, $spafe62e);
|
$sp2d97e8 = CreateCompanyToken::dispatchNow($sp035a66, $spaa9f78, $spafe62e);
|
||||||
|
|
||||||
|
@ -221,8 +221,8 @@ class CompanyImport implements ShouldQueue
|
|||||||
private function unzipFile()
|
private function unzipFile()
|
||||||
{
|
{
|
||||||
|
|
||||||
if(mime_content_type(Storage::path($this->file_location)) == 'text/plain')
|
// if(mime_content_type(Storage::path($this->file_location)) == 'text/plain')
|
||||||
return Storage::path($this->file_location);
|
// return Storage::path($this->file_location);
|
||||||
|
|
||||||
$path = TempFile::filePath(Storage::get($this->file_location), basename($this->file_location));
|
$path = TempFile::filePath(Storage::get($this->file_location), basename($this->file_location));
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ class SubscriptionCron
|
|||||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
->where('balance', '>', 0)
|
->where('balance', '>', 0)
|
||||||
->whereDate('due_date', '<=', now()->addDay()->startOfDay())
|
->whereDate('due_date', '<=', now()->addDay()->startOfDay())
|
||||||
|
->whereNull('deleted_at')
|
||||||
->whereNotNull('subscription_id')
|
->whereNotNull('subscription_id')
|
||||||
->cursor();
|
->cursor();
|
||||||
|
|
||||||
|
@ -86,9 +86,8 @@ class CreateEntityPdf implements ShouldQueue
|
|||||||
|
|
||||||
$this->contact = $invitation->contact;
|
$this->contact = $invitation->contact;
|
||||||
|
|
||||||
$this->disk = $disk;
|
$this->disk = Ninja::isHosted() ? config('filesystems.default') : $disk;
|
||||||
|
|
||||||
// $this->disk = $disk ?? config('filesystems.default');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
@ -201,11 +200,9 @@ class CreateEntityPdf implements ShouldQueue
|
|||||||
|
|
||||||
if(!Storage::disk($this->disk)->exists($path))
|
if(!Storage::disk($this->disk)->exists($path))
|
||||||
Storage::disk($this->disk)->makeDirectory($path, 0775);
|
Storage::disk($this->disk)->makeDirectory($path, 0775);
|
||||||
|
|
||||||
|
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||||
|
|
||||||
nlog($file_path);
|
|
||||||
|
|
||||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(\Exception $e)
|
catch(\Exception $e)
|
||||||
{
|
{
|
||||||
|
@ -190,6 +190,9 @@ class Import implements ShouldQueue
|
|||||||
{
|
{
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
|
|
||||||
|
nlog("Starting Migration");
|
||||||
|
nlog($this->user->email);
|
||||||
|
|
||||||
auth()->login($this->user, false);
|
auth()->login($this->user, false);
|
||||||
auth()->user()->setCompany($this->company);
|
auth()->user()->setCompany($this->company);
|
||||||
|
|
||||||
@ -333,7 +336,7 @@ class Import implements ShouldQueue
|
|||||||
|
|
||||||
$data = $this->transformCompanyData($data);
|
$data = $this->transformCompanyData($data);
|
||||||
|
|
||||||
if(Ninja::isHosted() && strlen($data['subdomain']) > 1) {
|
if(Ninja::isHosted()) {
|
||||||
|
|
||||||
if(!MultiDB::checkDomainAvailable($data['subdomain']))
|
if(!MultiDB::checkDomainAvailable($data['subdomain']))
|
||||||
$data['subdomain'] = MultiDB::randomSubdomainGenerator();
|
$data['subdomain'] = MultiDB::randomSubdomainGenerator();
|
||||||
@ -364,6 +367,10 @@ class Import implements ShouldQueue
|
|||||||
unset($data['referral_code']);
|
unset($data['referral_code']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($data['custom_fields']) && is_array($data['custom_fields'])) {
|
||||||
|
$data['custom_fields'] = $this->parseCustomFields($data['custom_fields']);
|
||||||
|
}
|
||||||
|
|
||||||
$company_repository = new CompanyRepository();
|
$company_repository = new CompanyRepository();
|
||||||
$company_repository->save($data, $this->company);
|
$company_repository->save($data, $this->company);
|
||||||
|
|
||||||
@ -385,6 +392,34 @@ class Import implements ShouldQueue
|
|||||||
$company_repository = null;
|
$company_repository = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function parseCustomFields($fields) :array
|
||||||
|
{
|
||||||
|
|
||||||
|
if(array_key_exists('account1', $fields))
|
||||||
|
$fields['company1'] = $fields['account1'];
|
||||||
|
|
||||||
|
if(array_key_exists('account2', $fields))
|
||||||
|
$fields['company2'] = $fields['account2'];
|
||||||
|
|
||||||
|
if(array_key_exists('invoice1', $fields))
|
||||||
|
$fields['surcharge1'] = $fields['invoice1'];
|
||||||
|
|
||||||
|
if(array_key_exists('invoice2', $fields))
|
||||||
|
$fields['surcharge2'] = $fields['invoice2'];
|
||||||
|
|
||||||
|
if(array_key_exists('invoice_text1', $fields))
|
||||||
|
$fields['invoice1'] = $fields['invoice_text1'];
|
||||||
|
|
||||||
|
if(array_key_exists('invoice_text2', $fields))
|
||||||
|
$fields['invoice2'] = $fields['invoice_text2'];
|
||||||
|
|
||||||
|
foreach ($fields as &$value) {
|
||||||
|
$value = (string) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
private function transformCompanyData(array $data): array
|
private function transformCompanyData(array $data): array
|
||||||
{
|
{
|
||||||
$company_settings = CompanySettings::defaults();
|
$company_settings = CompanySettings::defaults();
|
||||||
@ -1321,7 +1356,7 @@ class Import implements ShouldQueue
|
|||||||
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
|
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
|
if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
|
||||||
$modified['gateway_key'] = 'd14dd26a47cecc30fdd65700bfb67b34';
|
$modified['gateway_key'] = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||||
$modified['fees_and_limits'] = [];
|
$modified['fees_and_limits'] = [];
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
|||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
class VersionCheck implements ShouldQueue
|
class VersionCheck implements ShouldQueue
|
||||||
{
|
{
|
||||||
@ -49,7 +50,7 @@ class VersionCheck implements ShouldQueue
|
|||||||
if(!$account)
|
if(!$account)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if($account->plan == 'white_label' && $account->plan_expires && $account->plan_expires->lt(now())){
|
if($account->plan == 'white_label' && $account->plan_expires && Carbon::parse($account->plan_expires)->lt(now())){
|
||||||
$account->plan = null;
|
$account->plan = null;
|
||||||
$account->plan_expires = null;
|
$account->plan_expires = null;
|
||||||
$account->save();
|
$account->save();
|
||||||
|
@ -50,7 +50,7 @@ $user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars[
|
|||||||
$fields->subscription_id = $subscription->id;
|
$fields->subscription_id = $subscription->id;
|
||||||
$fields->user_id = $user_id;
|
$fields->user_id = $user_id;
|
||||||
$fields->company_id = $subscription->company_id;
|
$fields->company_id = $subscription->company_id;
|
||||||
$fields->activity_type_id = Activity::ARCHIVE_SUBSCRIPTIOn;
|
$fields->activity_type_id = Activity::ARCHIVE_SUBSCRIPTION;
|
||||||
|
|
||||||
$this->activity_repo->save($fields, $subscription, $event->event_vars);
|
$this->activity_repo->save($fields, $subscription, $event->event_vars);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,6 @@ class PaymentNotification implements ShouldQueue
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*Google Analytics Track Revenue*/
|
/*Google Analytics Track Revenue*/
|
||||||
if (isset($payment->company->google_analytics_key)) {
|
if (isset($payment->company->google_analytics_key)) {
|
||||||
$this->trackRevenue($event);
|
$this->trackRevenue($event);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace App\Mail\Engine;
|
namespace App\Mail\Engine;
|
||||||
|
|
||||||
|
use App\Models\Account;
|
||||||
use App\Utils\HtmlEngine;
|
use App\Utils\HtmlEngine;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Number;
|
use App\Utils\Number;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace App\Mail\Engine;
|
namespace App\Mail\Engine;
|
||||||
|
|
||||||
use App\DataMapper\EmailTemplateDefaults;
|
use App\DataMapper\EmailTemplateDefaults;
|
||||||
|
use App\Jobs\Entity\CreateEntityPdf;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use App\Utils\HtmlEngine;
|
use App\Utils\HtmlEngine;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
@ -116,8 +117,6 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
|||||||
else
|
else
|
||||||
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation)]);
|
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation)]);
|
||||||
|
|
||||||
// $this->setAttachments(['path' => $this->invoice->pdf_file_path(), 'name' => basename($this->invoice->pdf_file_path())]);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//attach third party documents
|
//attach third party documents
|
||||||
|
@ -52,7 +52,7 @@ class SupportMessageSent extends Mailable
|
|||||||
|
|
||||||
$account = auth()->user()->account;
|
$account = auth()->user()->account;
|
||||||
|
|
||||||
$plan = $account->plan ?: 'Free Self Hosted';
|
$plan = $account->plan ?: 'Forever Free';
|
||||||
|
|
||||||
$company = auth()->user()->company();
|
$company = auth()->user()->company();
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
@ -55,7 +55,7 @@ class Account extends BaseModel
|
|||||||
'promo_expires',
|
'promo_expires',
|
||||||
'discount_expires',
|
'discount_expires',
|
||||||
'trial_started',
|
'trial_started',
|
||||||
'plan_expires'
|
// 'plan_expires'
|
||||||
];
|
];
|
||||||
|
|
||||||
const PLAN_FREE = 'free';
|
const PLAN_FREE = 'free';
|
||||||
@ -120,6 +120,11 @@ class Account extends BaseModel
|
|||||||
return $this->hasMany(CompanyUser::class);
|
return $this->hasMany(CompanyUser::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function owner()
|
||||||
|
{
|
||||||
|
return $this->hasMany(CompanyUser::class)->where('is_owner', true)->first() ? $this->hasMany(CompanyUser::class)->where('is_owner', true)->first()->user : false;
|
||||||
|
}
|
||||||
|
|
||||||
public function getPlan()
|
public function getPlan()
|
||||||
{
|
{
|
||||||
return $this->plan ?: '';
|
return $this->plan ?: '';
|
||||||
|
@ -13,6 +13,8 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\DataMapper\ClientSettings;
|
use App\DataMapper\ClientSettings;
|
||||||
use App\DataMapper\CompanySettings;
|
use App\DataMapper\CompanySettings;
|
||||||
|
use App\DataMapper\FeesAndLimits;
|
||||||
|
use App\Models\CompanyGateway;
|
||||||
use App\Models\Presenters\ClientPresenter;
|
use App\Models\Presenters\ClientPresenter;
|
||||||
use App\Services\Client\ClientService;
|
use App\Services\Client\ClientService;
|
||||||
use App\Utils\Traits\AppSetup;
|
use App\Utils\Traits\AppSetup;
|
||||||
@ -396,61 +398,131 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
*/
|
*/
|
||||||
public function getCreditCardGateway() :?CompanyGateway
|
public function getCreditCardGateway() :?CompanyGateway
|
||||||
{
|
{
|
||||||
$company_gateways = $this->getSetting('company_gateway_ids');
|
// $company_gateways = $this->getSetting('company_gateway_ids');
|
||||||
|
|
||||||
/* It is very important to respect the order of the company_gateway_ids as they are ordered by priority*/
|
// /* It is very important to respect the order of the company_gateway_ids as they are ordered by priority*/
|
||||||
if (strlen($company_gateways) >= 1) {
|
// if (strlen($company_gateways) >= 1) {
|
||||||
$transformed_ids = $this->transformKeys(explode(',', $company_gateways));
|
// $transformed_ids = $this->transformKeys(explode(',', $company_gateways));
|
||||||
$gateways = $this->company
|
// $gateways = $this->company
|
||||||
->company_gateways
|
// ->company_gateways
|
||||||
->whereIn('id', $transformed_ids)
|
// ->whereIn('id', $transformed_ids)
|
||||||
->sortby(function ($model) use ($transformed_ids) {
|
// ->sortby(function ($model) use ($transformed_ids) {
|
||||||
return array_search($model->id, $transformed_ids);
|
// return array_search($model->id, $transformed_ids);
|
||||||
});
|
// });
|
||||||
} else {
|
// } else {
|
||||||
$gateways = $this->company->company_gateways;
|
// $gateways = $this->company->company_gateways;
|
||||||
}
|
// }
|
||||||
|
|
||||||
foreach ($gateways as $gateway) {
|
// foreach ($gateways as $gateway) {
|
||||||
if (in_array(GatewayType::CREDIT_CARD, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::CREDIT_CARD))) {
|
// if (in_array(GatewayType::CREDIT_CARD, $gateway->driver($this)->gatewayTypeEnabled($gateway, GatewayType::CREDIT_CARD))) {
|
||||||
return $gateway;
|
// return $gateway;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return null;
|
||||||
|
//
|
||||||
|
|
||||||
|
$pms = $this->service()->getPaymentMethods(0);
|
||||||
|
|
||||||
|
foreach($pms as $pm)
|
||||||
|
{
|
||||||
|
|
||||||
|
if($pm['gateway_type_id'] == GatewayType::CREDIT_CARD)
|
||||||
|
{
|
||||||
|
$cg = CompanyGateway::find($pm['company_gateway_id']);
|
||||||
|
|
||||||
|
if($cg && !property_exists($cg->fees_and_limits, GatewayType::CREDIT_CARD)){
|
||||||
|
$fees_and_limits = $cg->fees_and_limits;
|
||||||
|
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
||||||
|
$cg->fees_and_limits = $fees_and_limits;
|
||||||
|
$cg->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($cg && $cg->fees_and_limits->{GatewayType::CREDIT_CARD}->is_enabled)
|
||||||
|
return $cg;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//todo refactor this - it is only searching for existing tokens
|
||||||
public function getBankTransferGateway() :?CompanyGateway
|
public function getBankTransferGateway() :?CompanyGateway
|
||||||
{
|
{
|
||||||
$company_gateways = $this->getSetting('company_gateway_ids');
|
$pms = $this->service()->getPaymentMethods(0);
|
||||||
|
|
||||||
|
if($this->currency()->code == 'USD' && in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id'))){
|
||||||
|
|
||||||
|
foreach($pms as $pm){
|
||||||
|
|
||||||
|
if($pm['gateway_type_id'] == GatewayType::BANK_TRANSFER)
|
||||||
|
{
|
||||||
|
$cg = CompanyGateway::find($pm['company_gateway_id']);
|
||||||
|
|
||||||
|
if($cg && !property_exists($cg->fees_and_limits, GatewayType::BANK_TRANSFER)){
|
||||||
|
$fees_and_limits = $cg->fees_and_limits;
|
||||||
|
$fees_and_limits->{GatewayType::BANK_TRANSFER} = new FeesAndLimits;
|
||||||
|
$cg->fees_and_limits = $fees_and_limits;
|
||||||
|
$cg->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($cg && $cg->fees_and_limits->{GatewayType::BANK_TRANSFER}->is_enabled)
|
||||||
|
return $cg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (strlen($company_gateways) >= 1) {
|
|
||||||
$transformed_ids = $this->transformKeys(explode(',', $company_gateways));
|
|
||||||
$gateways = $this->company
|
|
||||||
->company_gateways
|
|
||||||
->whereIn('id', $transformed_ids)
|
|
||||||
->sortby(function ($model) use ($transformed_ids) {
|
|
||||||
return array_search($model->id, $transformed_ids);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$gateways = $this->company->company_gateways;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($gateways as $gateway) {
|
if($this->currency()->code == 'EUR' && in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id'))){
|
||||||
if ($this->currency()->code == 'USD' && in_array(GatewayType::BANK_TRANSFER, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::BANK_TRANSFER))) {
|
|
||||||
return $gateway;
|
foreach($pms as $pm){
|
||||||
|
|
||||||
|
if($pm['gateway_type_id'] == GatewayType::SEPA)
|
||||||
|
{
|
||||||
|
$cg = CompanyGateway::find($pm['company_gateway_id']);
|
||||||
|
|
||||||
|
if($cg && $cg->fees_and_limits->{GatewayType::SEPA}->is_enabled)
|
||||||
|
return $cg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->currency()->code == 'EUR' && in_array(GatewayType::SEPA, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::SEPA))) {
|
|
||||||
return $gateway;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
// $company_gateways = $this->getSetting('company_gateway_ids');
|
||||||
|
|
||||||
|
// if (strlen($company_gateways) >= 1) {
|
||||||
|
// $transformed_ids = $this->transformKeys(explode(',', $company_gateways));
|
||||||
|
// $gateways = $this->company
|
||||||
|
// ->company_gateways
|
||||||
|
// ->whereIn('id', $transformed_ids)
|
||||||
|
// ->sortby(function ($model) use ($transformed_ids) {
|
||||||
|
// return array_search($model->id, $transformed_ids);
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// $gateways = $this->company->company_gateways;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// foreach ($gateways as $gateway) {
|
||||||
|
// if ($this->currency()->code == 'USD' && in_array(GatewayType::BANK_TRANSFER, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::BANK_TRANSFER))) {
|
||||||
|
// return $gateway;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if ($this->currency()->code == 'EUR' && in_array(GatewayType::SEPA, $gateway->driver($this)->gatewayTypeEnabled(GatewayType::SEPA))) {
|
||||||
|
// return $gateway;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBankTransferMethodType()
|
public function getBankTransferMethodType()
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($this->currency()->code == 'USD') {
|
if ($this->currency()->code == 'USD') {
|
||||||
return GatewayType::BANK_TRANSFER;
|
return GatewayType::BANK_TRANSFER;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ use App\Models\Presenters\CompanyPresenter;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\Notification\NotificationService;
|
use App\Services\Notification\NotificationService;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
|
use App\Utils\Traits\AppSetup;
|
||||||
use App\Utils\Traits\CompanySettingsSaver;
|
use App\Utils\Traits\CompanySettingsSaver;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use App\Utils\Traits\ThrottlesEmail;
|
use App\Utils\Traits\ThrottlesEmail;
|
||||||
@ -31,6 +32,7 @@ class Company extends BaseModel
|
|||||||
use MakesHash;
|
use MakesHash;
|
||||||
use CompanySettingsSaver;
|
use CompanySettingsSaver;
|
||||||
use ThrottlesEmail;
|
use ThrottlesEmail;
|
||||||
|
use AppSetup;
|
||||||
|
|
||||||
const ENTITY_RECURRING_INVOICE = 'recurring_invoice';
|
const ENTITY_RECURRING_INVOICE = 'recurring_invoice';
|
||||||
const ENTITY_CREDIT = 'credit';
|
const ENTITY_CREDIT = 'credit';
|
||||||
@ -311,7 +313,17 @@ class Company extends BaseModel
|
|||||||
|
|
||||||
public function timezone()
|
public function timezone()
|
||||||
{
|
{
|
||||||
return Timezone::find($this->settings->timezone_id);
|
|
||||||
|
$timezones = Cache::get('timezones');
|
||||||
|
|
||||||
|
if(!$timezones)
|
||||||
|
$this->buildCache(true);
|
||||||
|
|
||||||
|
return $timezones->filter(function ($item) {
|
||||||
|
return $item->id == $this->settings->timezone_id;
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
// return Timezone::find($this->settings->timezone_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function designs()
|
public function designs()
|
||||||
@ -339,7 +351,18 @@ class Company extends BaseModel
|
|||||||
*/
|
*/
|
||||||
public function language()
|
public function language()
|
||||||
{
|
{
|
||||||
return Language::find($this->settings->language_id);
|
|
||||||
|
$languages = Cache::get('languages');
|
||||||
|
|
||||||
|
if(!$languages)
|
||||||
|
$this->buildCache(true);
|
||||||
|
|
||||||
|
return $languages->filter(function ($item) {
|
||||||
|
return $item->id == $this->settings->language_id;
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
|
||||||
|
// return Language::find($this->settings->language_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLocale()
|
public function getLocale()
|
||||||
|
@ -59,6 +59,17 @@ class CompanyGateway extends BaseModel
|
|||||||
16 => ['card' => 'images/credit_cards/Test-Discover-Icon.png', 'text' => 'Discover'],
|
16 => ['card' => 'images/credit_cards/Test-Discover-Icon.png', 'text' => 'Discover'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// const TYPE_PAYPAL = 300;
|
||||||
|
// const TYPE_STRIPE = 301;
|
||||||
|
// const TYPE_LEDGER = 302;
|
||||||
|
// const TYPE_FAILURE = 303;
|
||||||
|
// const TYPE_CHECKOUT = 304;
|
||||||
|
// const TYPE_AUTHORIZE = 305;
|
||||||
|
// const TYPE_CUSTOM = 306;
|
||||||
|
// const TYPE_BRAINTREE = 307;
|
||||||
|
// const TYPE_WEPAY = 309;
|
||||||
|
|
||||||
public $gateway_consts = [
|
public $gateway_consts = [
|
||||||
'38f2c48af60c7dd69e04248cbb24c36e' => 300,
|
'38f2c48af60c7dd69e04248cbb24c36e' => 300,
|
||||||
'd14dd26a37cecc30fdd65700bfb55b23' => 301,
|
'd14dd26a37cecc30fdd65700bfb55b23' => 301,
|
||||||
@ -66,6 +77,8 @@ class CompanyGateway extends BaseModel
|
|||||||
'3b6621f970ab18887c4f6dca78d3f8bb' => 305,
|
'3b6621f970ab18887c4f6dca78d3f8bb' => 305,
|
||||||
'54faab2ab6e3223dbe848b1686490baa' => 306,
|
'54faab2ab6e3223dbe848b1686490baa' => 306,
|
||||||
'd14dd26a47cecc30fdd65700bfb67b34' => 301,
|
'd14dd26a47cecc30fdd65700bfb67b34' => 301,
|
||||||
|
'8fdeed552015b3c7b44ed6c8ebd9e992' => 309,
|
||||||
|
'f7ec488676d310683fb51802d076d713' => 307,
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $touches = [];
|
protected $touches = [];
|
||||||
@ -358,6 +371,11 @@ class CompanyGateway extends BaseModel
|
|||||||
return $fee;
|
return $fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function webhookUrl()
|
||||||
|
{
|
||||||
|
return route('payment_webhook', ['company_key' => $this->company->company_key, 'company_gateway_id' => $this->hashed_id]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* we need to average out the gateway fees across all the invoices
|
* we need to average out the gateway fees across all the invoices
|
||||||
* so lets iterate.
|
* so lets iterate.
|
||||||
@ -399,4 +417,6 @@ class CompanyGateway extends BaseModel
|
|||||||
return $this
|
return $this
|
||||||
->where('id', $this->decodePrimaryKey($value))->firstOrFail();
|
->where('id', $this->decodePrimaryKey($value))->firstOrFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,10 @@ class UserPresenter extends EntityPresenter
|
|||||||
*/
|
*/
|
||||||
public function name()
|
public function name()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if(!$this->entity)
|
||||||
|
return "No User Object Available";
|
||||||
|
|
||||||
$first_name = isset($this->entity->first_name) ? $this->entity->first_name : '';
|
$first_name = isset($this->entity->first_name) ? $this->entity->first_name : '';
|
||||||
$last_name = isset($this->entity->last_name) ? $this->entity->last_name : '';
|
$last_name = isset($this->entity->last_name) ? $this->entity->last_name : '';
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ class SystemLog extends Model
|
|||||||
const TYPE_CUSTOM = 306;
|
const TYPE_CUSTOM = 306;
|
||||||
const TYPE_BRAINTREE = 307;
|
const TYPE_BRAINTREE = 307;
|
||||||
const TYPE_WEPAY = 309;
|
const TYPE_WEPAY = 309;
|
||||||
|
const TYPE_PAYFAST = 310;
|
||||||
|
|
||||||
|
|
||||||
const TYPE_QUOTA_EXCEEDED = 400;
|
const TYPE_QUOTA_EXCEEDED = 400;
|
||||||
|
@ -161,6 +161,8 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
public function setCompany($company)
|
public function setCompany($company)
|
||||||
{
|
{
|
||||||
$this->company = $company;
|
$this->company = $company;
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,7 +119,7 @@ class AuthorizeCreditCard
|
|||||||
'data' => $this->formatGatewayResponse($data, $vars),
|
'data' => $this->formatGatewayResponse($data, $vars),
|
||||||
];
|
];
|
||||||
|
|
||||||
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_AUTHORIZE, $this->authorize->client);
|
SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -202,6 +202,7 @@ class AuthorizeCreditCard
|
|||||||
private function processFailedResponse($data, $request)
|
private function processFailedResponse($data, $request)
|
||||||
{
|
{
|
||||||
$response = $data['response'];
|
$response = $data['response'];
|
||||||
|
$amount = array_key_exists('amount_with_fee', $data) ? $data['amount_with_fee'] : 0;
|
||||||
|
|
||||||
PaymentFailureMailer::dispatch($this->authorize->client, $response->getTransactionResponse()->getTransId(), $this->authorize->client->company, $data['amount_with_fee']);
|
PaymentFailureMailer::dispatch($this->authorize->client, $response->getTransactionResponse()->getTransId(), $this->authorize->client->company, $data['amount_with_fee']);
|
||||||
|
|
||||||
|
@ -548,6 +548,15 @@ class BaseDriver extends AbstractPaymentDriver
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function genericWebhookUrl()
|
||||||
|
{
|
||||||
|
return route('payment_notification_webhook', [
|
||||||
|
'company_key' => $this->client->company->company_key,
|
||||||
|
'company_gateway_id' => $this->encodePrimaryKey($this->company_gateway->id),
|
||||||
|
'client' => $this->encodePrimaryKey($this->client->id),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/* Performs an extra iterate on the gatewayTypes() array and passes back only the enabled gateways*/
|
/* Performs an extra iterate on the gatewayTypes() array and passes back only the enabled gateways*/
|
||||||
public function gatewayTypeEnabled($type)
|
public function gatewayTypeEnabled($type)
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace App\PaymentDrivers;
|
namespace App\PaymentDrivers;
|
||||||
|
|
||||||
|
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||||
use App\Models\ClientGatewayToken;
|
use App\Models\ClientGatewayToken;
|
||||||
use App\Models\GatewayType;
|
use App\Models\GatewayType;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
@ -39,6 +40,22 @@ class DriverTemplate extends BaseDriver
|
|||||||
|
|
||||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_STRIPE; //define a constant for your gateway ie TYPE_YOUR_CUSTOM_GATEWAY - set the const in the SystemLog model
|
const SYSTEM_LOG_TYPE = SystemLog::TYPE_STRIPE; //define a constant for your gateway ie TYPE_YOUR_CUSTOM_GATEWAY - set the const in the SystemLog model
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
return $this; /* This is where you boot the gateway with your auth credentials*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns an array of gateway types for the payment gateway */
|
||||||
|
public function gatewayTypes(): array
|
||||||
|
{
|
||||||
|
$types = [];
|
||||||
|
|
||||||
|
$types[] = GatewayType::CREDIT_CARD;
|
||||||
|
|
||||||
|
return $types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets the payment method initialized */
|
||||||
public function setPaymentMethod($payment_method_id)
|
public function setPaymentMethod($payment_method_id)
|
||||||
{
|
{
|
||||||
$class = self::$methods[$payment_method_id];
|
$class = self::$methods[$payment_method_id];
|
||||||
@ -75,4 +92,8 @@ class DriverTemplate extends BaseDriver
|
|||||||
{
|
{
|
||||||
return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here
|
return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
282
app/PaymentDrivers/PayFast/CreditCard.php
Normal file
282
app/PaymentDrivers/PayFast/CreditCard.php
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\PaymentDrivers\PayFast;
|
||||||
|
|
||||||
|
use App\Exceptions\PaymentFailed;
|
||||||
|
use App\Jobs\Mail\PaymentFailureMailer;
|
||||||
|
use App\Jobs\Util\SystemLogger;
|
||||||
|
use App\Models\ClientGatewayToken;
|
||||||
|
use App\Models\GatewayType;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\PaymentHash;
|
||||||
|
use App\Models\PaymentType;
|
||||||
|
use App\Models\SystemLog;
|
||||||
|
use App\PaymentDrivers\PayFastPaymentDriver;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class CreditCard
|
||||||
|
{
|
||||||
|
|
||||||
|
public $payfast;
|
||||||
|
|
||||||
|
public function __construct(PayFastPaymentDriver $payfast)
|
||||||
|
{
|
||||||
|
$this->payfast = $payfast;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
$data = array();
|
||||||
|
$data['merchant_id'] = $this->getMerchantId();
|
||||||
|
$data['merchant_key'] = $this->getMerchantKey();
|
||||||
|
$data['return_url'] = $this->getReturnUrl();
|
||||||
|
$data['cancel_url'] = $this->getCancelUrl();
|
||||||
|
$data['notify_url'] = $this->getNotifyUrl();
|
||||||
|
|
||||||
|
if ($this->getCard()) {
|
||||||
|
$data['name_first'] = $this->getCard()->getFirstName();
|
||||||
|
$data['name_last'] = $this->getCard()->getLastName();
|
||||||
|
$data['email_address'] = $this->getCard()->getEmail();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['m_payment_id'] = $this->getTransactionId();
|
||||||
|
$data['amount'] = $this->getAmount();
|
||||||
|
$data['item_name'] = $this->getDescription();
|
||||||
|
$data['custom_int1'] = $this->getCustomInt1();
|
||||||
|
$data['custom_int2'] = $this->getCustomInt2();
|
||||||
|
$data['custom_int3'] = $this->getCustomInt3();
|
||||||
|
$data['custom_int4'] = $this->getCustomInt4();
|
||||||
|
$data['custom_int5'] = $this->getCustomInt5();
|
||||||
|
$data['custom_str1'] = $this->getCustomStr1();
|
||||||
|
$data['custom_str2'] = $this->getCustomStr2();
|
||||||
|
$data['custom_str3'] = $this->getCustomStr3();
|
||||||
|
$data['custom_str4'] = $this->getCustomStr4();
|
||||||
|
$data['custom_str5'] = $this->getCustomStr5();
|
||||||
|
|
||||||
|
if ($this->getPaymentMethod()) {
|
||||||
|
$data['payment_method'] = $this->getPaymentMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 == $this->getSubscriptionType()) {
|
||||||
|
$data['subscription_type'] = $this->getSubscriptionType();
|
||||||
|
$data['billing_date'] = $this->getBillingDate();
|
||||||
|
$data['recurring_amount'] = $this->getRecurringAmount();
|
||||||
|
$data['frequency'] = $this->getFrequency();
|
||||||
|
$data['cycles'] = $this->getCycles();
|
||||||
|
}
|
||||||
|
if (2 == $this->getSubscriptionType()) {
|
||||||
|
$data['subscription_type'] = $this->getSubscriptionType();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['passphrase'] = $this->getParameter('passphrase'); 123456789012aV
|
||||||
|
$data['signature'] = $this->generateSignature($data);
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function authorizeView($data)
|
||||||
|
{
|
||||||
|
$hash = Str::random(32);
|
||||||
|
|
||||||
|
Cache::put($hash, 'cc_auth', 300);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'merchant_id' => $this->payfast->company_gateway->getConfigField('merchantId'),
|
||||||
|
'merchant_key' => $this->payfast->company_gateway->getConfigField('merchantKey'),
|
||||||
|
'return_url' => route('client.payment_methods.index'),
|
||||||
|
'cancel_url' => route('client.payment_methods.index'),
|
||||||
|
'notify_url' => $this->payfast->genericWebhookUrl(),
|
||||||
|
'm_payment_id' => $hash,
|
||||||
|
'amount' => 5,
|
||||||
|
'item_name' => 'pre-auth',
|
||||||
|
'item_description' => 'Credit Card Pre Authorization',
|
||||||
|
'subscription_type' => 2,
|
||||||
|
'passphrase' => $this->payfast->company_gateway->getConfigField('passphrase'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$data['signature'] = $this->payfast->generateSignature($data);
|
||||||
|
$data['gateway'] = $this->payfast;
|
||||||
|
$data['payment_endpoint_url'] = $this->payfast->endpointUrl();
|
||||||
|
|
||||||
|
return render('gateways.payfast.authorize', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
'm_payment_id' => NULL,
|
||||||
|
'pf_payment_id' => '1409993',
|
||||||
|
'payment_status' => 'COMPLETE',
|
||||||
|
'item_name' => 'pre-auth',
|
||||||
|
'item_description' => NULL,
|
||||||
|
'amount_gross' => '5.00',
|
||||||
|
'amount_fee' => '-2.53',
|
||||||
|
'amount_net' => '2.47',
|
||||||
|
'custom_str1' => NULL,
|
||||||
|
'custom_str2' => NULL,
|
||||||
|
'custom_str3' => NULL,
|
||||||
|
'custom_str4' => NULL,
|
||||||
|
'custom_str5' => NULL,
|
||||||
|
'custom_int1' => NULL,
|
||||||
|
'custom_int2' => NULL,
|
||||||
|
'custom_int3' => NULL,
|
||||||
|
'custom_int4' => NULL,
|
||||||
|
'custom_int5' => NULL,
|
||||||
|
'name_first' => NULL,
|
||||||
|
'name_last' => NULL,
|
||||||
|
'email_address' => NULL,
|
||||||
|
'merchant_id' => '10023100',
|
||||||
|
'token' => '34b66bc2-3c54-9590-03ea-42ee8b89922a',
|
||||||
|
'billing_date' => '2021-07-05',
|
||||||
|
'signature' => 'ebdb4ca937d0e3f43462841c0afc6ad9',
|
||||||
|
'q' => '/payment_notification_webhook/EhbnVYyzJZyccY85hcHIkIzNPI2rtHzznAyyyG73oSxZidAdN9gf8BvAKDomqeHp/4openRe7Az/WPe99p3eLy',
|
||||||
|
*/
|
||||||
|
public function authorizeResponse($request)
|
||||||
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
|
||||||
|
$cgt = [];
|
||||||
|
$cgt['token'] = $data['token'];
|
||||||
|
$cgt['payment_method_id'] = GatewayType::CREDIT_CARD;
|
||||||
|
|
||||||
|
$payment_meta = new \stdClass;
|
||||||
|
$payment_meta->exp_month = 'xx';
|
||||||
|
$payment_meta->exp_year = 'xx';
|
||||||
|
$payment_meta->brand = 'CC';
|
||||||
|
$payment_meta->last4 = 'xxxx';
|
||||||
|
$payment_meta->type = GatewayType::CREDIT_CARD;
|
||||||
|
|
||||||
|
$cgt['payment_meta'] = $payment_meta;
|
||||||
|
|
||||||
|
$token = $this->payfast->storeGatewayToken($cgt, []);
|
||||||
|
|
||||||
|
return response()->json([], 200);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function paymentView($data)
|
||||||
|
{
|
||||||
|
|
||||||
|
$payfast_data = [
|
||||||
|
'merchant_id' => $this->payfast->company_gateway->getConfigField('merchantId'),
|
||||||
|
'merchant_key' => $this->payfast->company_gateway->getConfigField('merchantKey'),
|
||||||
|
'return_url' => route('client.payments.index'),
|
||||||
|
'cancel_url' => route('client.payment_methods.index'),
|
||||||
|
'notify_url' => $this->payfast->genericWebhookUrl(),
|
||||||
|
'm_payment_id' => $data['payment_hash'],
|
||||||
|
'amount' => $data['amount_with_fee'],
|
||||||
|
'item_name' => 'purchase',
|
||||||
|
'item_description' => ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number'),
|
||||||
|
'passphrase' => $this->payfast->company_gateway->getConfigField('passphrase'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$payfast_data['signature'] = $this->payfast->generateSignature($payfast_data);
|
||||||
|
$payfast_data['gateway'] = $this->payfast;
|
||||||
|
$payfast_data['payment_endpoint_url'] = $this->payfast->endpointUrl();
|
||||||
|
|
||||||
|
return render('gateways.payfast.pay', array_merge($data, $payfast_data));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
[2021-07-05 11:21:24] local.INFO: array (
|
||||||
|
'm_payment_id' => 'B7G9Q2vPhqkLEoMwwY1paXvPGuFxpbDe',
|
||||||
|
'pf_payment_id' => '1410364',
|
||||||
|
'payment_status' => 'COMPLETE',
|
||||||
|
'item_name' => 'purchase',
|
||||||
|
'item_description' => 'Invoices: ["0001"]',
|
||||||
|
'amount_gross' => '100.00',
|
||||||
|
'amount_fee' => '-2.30',
|
||||||
|
'amount_net' => '97.70',
|
||||||
|
'custom_str1' => NULL,
|
||||||
|
'custom_str2' => NULL,
|
||||||
|
'custom_str3' => NULL,
|
||||||
|
'custom_str4' => NULL,
|
||||||
|
'custom_str5' => NULL,
|
||||||
|
'custom_int1' => NULL,
|
||||||
|
'custom_int2' => NULL,
|
||||||
|
'custom_int3' => NULL,
|
||||||
|
'custom_int4' => NULL,
|
||||||
|
'custom_int5' => NULL,
|
||||||
|
'name_first' => NULL,
|
||||||
|
'name_last' => NULL,
|
||||||
|
'email_address' => NULL,
|
||||||
|
'merchant_id' => '10023100',
|
||||||
|
'signature' => '3ed27638479fd65cdffb0f4910679d10',
|
||||||
|
'q' => '/payment_notification_webhook/EhbnVYyzJZyccY85hcHIkIzNPI2rtHzznAyyyG73oSxZidAdN9gf8BvAKDomqeHp/4openRe7Az/WPe99p3eLy',
|
||||||
|
)
|
||||||
|
|
||||||
|
*/
|
||||||
|
public function paymentResponse(Request $request)
|
||||||
|
{
|
||||||
|
$response_array = $request->all();
|
||||||
|
|
||||||
|
$state = [
|
||||||
|
'server_response' => $request->all(),
|
||||||
|
'payment_hash' => $request->input('m_payment_id'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->payfast->payment_hash->data = array_merge((array) $this->payfast->payment_hash->data, $state);
|
||||||
|
$this->payfast->payment_hash->save();
|
||||||
|
|
||||||
|
if($response_array['payment_status'] == 'COMPLETE') {
|
||||||
|
|
||||||
|
$this->payfast->logSuccessfulGatewayResponse(['response' => $response_array, 'data' => $this->payfast->payment_hash], SystemLog::TYPE_PAYFAST);
|
||||||
|
|
||||||
|
return $this->processSuccessfulPayment($response_array);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->processUnsuccessfulPayment($response_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processSuccessfulPayment($response_array)
|
||||||
|
{
|
||||||
|
|
||||||
|
$payment_record = [];
|
||||||
|
$payment_record['amount'] = $response_array['amount_gross'];
|
||||||
|
$payment_record['payment_type'] = PaymentType::CREDIT_CARD_OTHER;
|
||||||
|
$payment_record['gateway_type_id'] = GatewayType::CREDIT_CARD;
|
||||||
|
$payment_record['transaction_reference'] = $response_array['pf_payment_id'];
|
||||||
|
|
||||||
|
$payment = $this->payfast->createPayment($payment_record, Payment::STATUS_COMPLETED);
|
||||||
|
|
||||||
|
return redirect()->route('client.payments.show', ['payment' => $this->payfast->encodePrimaryKey($payment->id)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processUnsuccessfulPayment($server_response)
|
||||||
|
{
|
||||||
|
PaymentFailureMailer::dispatch($this->payfast->client, $server_response->cancellation_reason, $this->payfast->client->company, $server_response->amount);
|
||||||
|
|
||||||
|
PaymentFailureMailer::dispatch(
|
||||||
|
$this->payfast->client,
|
||||||
|
$server_response,
|
||||||
|
$this->payfast->client->company,
|
||||||
|
$server_response['amount_gross']
|
||||||
|
);
|
||||||
|
|
||||||
|
$message = [
|
||||||
|
'server_response' => $server_response,
|
||||||
|
'data' => $this->payfast->payment_hash->data,
|
||||||
|
];
|
||||||
|
|
||||||
|
SystemLogger::dispatch(
|
||||||
|
$message,
|
||||||
|
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||||
|
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||||
|
SystemLog::TYPE_PAYFAST,
|
||||||
|
$this->payfast->client,
|
||||||
|
$this->payfast->client->company,
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new PaymentFailed('Failed to process the payment.', 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
184
app/PaymentDrivers/PayFast/Token.php
Normal file
184
app/PaymentDrivers/PayFast/Token.php
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\PaymentDrivers\PayFast;
|
||||||
|
|
||||||
|
use App\Exceptions\PaymentFailed;
|
||||||
|
use App\Jobs\Mail\PaymentFailureMailer;
|
||||||
|
use App\Jobs\Util\SystemLogger;
|
||||||
|
use App\Models\ClientGatewayToken;
|
||||||
|
use App\Models\GatewayType;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\PaymentHash;
|
||||||
|
use App\Models\PaymentType;
|
||||||
|
use App\Models\SystemLog;
|
||||||
|
use App\PaymentDrivers\PayFastPaymentDriver;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use GuzzleHttp\RequestOptions;
|
||||||
|
|
||||||
|
class Token
|
||||||
|
{
|
||||||
|
|
||||||
|
public $payfast;
|
||||||
|
|
||||||
|
//https://api.payfast.co.za/subscriptions/dc0521d3-55fe-269b-fa00-b647310d760f/adhoc
|
||||||
|
|
||||||
|
public function __construct(PayFastPaymentDriver $payfast)
|
||||||
|
{
|
||||||
|
$this->payfast = $payfast;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
// merchant-id
|
||||||
|
// integer, 8 char | REQUIRED
|
||||||
|
// Header, the Merchant ID as given by the PayFast system.
|
||||||
|
// version
|
||||||
|
// string | REQUIRED
|
||||||
|
// Header, the PayFast API version (i.e. v1).
|
||||||
|
// timestamp
|
||||||
|
// ISO-8601 date and time | REQUIRED
|
||||||
|
// Header, the current timestamp (YYYY-MM-DDTHH:MM:SS[+HH:MM]).
|
||||||
|
// signature
|
||||||
|
// string | REQUIRED
|
||||||
|
// Header, MD5 hash of the alphabetised submitted header and body variables, as well as the passphrase. Characters must be in lower case.
|
||||||
|
// amount
|
||||||
|
// integer | REQUIRED
|
||||||
|
// Body, the amount which the buyer must pay, in cents (ZAR), no decimals.
|
||||||
|
// item_name
|
||||||
|
// string, 100 char | REQUIRED
|
||||||
|
// Body, the name of the item being charged for.
|
||||||
|
// item_description
|
||||||
|
// string, 255 char | OPTIONAL
|
||||||
|
// Body, the description of the item being charged for.
|
||||||
|
// itn
|
||||||
|
// boolean | OPTIONAL
|
||||||
|
// Body, specify whether an ITN must be sent for the tokenization payment (true by default).
|
||||||
|
// m_payment_id
|
||||||
|
// string, 100 char | OPTIONAL
|
||||||
|
// Body, unique payment ID on the merchant’s system.
|
||||||
|
// cc_cvv
|
||||||
|
// numeric | OPTIONAL
|
||||||
|
|
||||||
|
|
||||||
|
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||||
|
{
|
||||||
|
|
||||||
|
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
|
||||||
|
$amount = round(($amount * pow(10, $this->payfast->client->currency()->precision)),0);
|
||||||
|
|
||||||
|
$header =[
|
||||||
|
'merchant-id' => $this->payfast->company_gateway->getConfigField('merchantId'),
|
||||||
|
'timestamp' => now()->format('c'),
|
||||||
|
'version' => 'v1',
|
||||||
|
];
|
||||||
|
|
||||||
|
nlog($header);
|
||||||
|
|
||||||
|
$body = [
|
||||||
|
'amount' => $amount,
|
||||||
|
'item_name' => 'purchase',
|
||||||
|
'item_description' => ctrans('texts.invoices') . ': ' . collect($payment_hash->invoices())->pluck('invoice_number'),
|
||||||
|
'm_payment_id' => $payment_hash->hash,
|
||||||
|
'passphrase' => $this->payfast->company_gateway->getConfigField('passphrase'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$header['signature'] = $this->genSig(array_merge($header, $body));
|
||||||
|
|
||||||
|
nlog($header['signature']);
|
||||||
|
nlog($header['timestamp']);
|
||||||
|
nlog($this->payfast->company_gateway->getConfigField('merchantId'));
|
||||||
|
|
||||||
|
$result = $this->send($header, $body, $cgt->token);
|
||||||
|
|
||||||
|
nlog($result);
|
||||||
|
|
||||||
|
// /*Refactor and push to BaseDriver*/
|
||||||
|
// if ($data['response'] != null && $data['response']->getMessages()->getResultCode() == 'Ok') {
|
||||||
|
|
||||||
|
// $response = $data['response'];
|
||||||
|
|
||||||
|
// $this->storePayment($payment_hash, $data);
|
||||||
|
|
||||||
|
// $vars = [
|
||||||
|
// 'invoices' => $payment_hash->invoices(),
|
||||||
|
// 'amount' => $amount,
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// $logger_message = [
|
||||||
|
// 'server_response' => $response->getTransactionResponse()->getTransId(),
|
||||||
|
// 'data' => $this->formatGatewayResponse($data, $vars),
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
|
||||||
|
|
||||||
|
// return true;
|
||||||
|
// } else {
|
||||||
|
|
||||||
|
// $vars = [
|
||||||
|
// 'invoices' => $payment_hash->invoices(),
|
||||||
|
// 'amount' => $amount,
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// $logger_message = [
|
||||||
|
// 'server_response' => $response->getTransactionResponse()->getTransId(),
|
||||||
|
// 'data' => $this->formatGatewayResponse($data, $vars),
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// PaymentFailureMailer::dispatch($this->authorize->client, $response->getTransactionResponse()->getTransId(), $this->authorize->client->company, $amount);
|
||||||
|
|
||||||
|
// SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_AUTHORIZE, $this->authorize->client, $this->authorize->client->company);
|
||||||
|
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
private function genSig($data)
|
||||||
|
{
|
||||||
|
$fields = [];
|
||||||
|
|
||||||
|
ksort($data);
|
||||||
|
|
||||||
|
foreach($data as $key => $value)
|
||||||
|
{
|
||||||
|
if (!empty($data[$key])) {
|
||||||
|
$fields[$key] = $data[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return md5(http_build_query($fields));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function send($headers, $body, $token)
|
||||||
|
{
|
||||||
|
$client = new \GuzzleHttp\Client(
|
||||||
|
[
|
||||||
|
'headers' => $headers,
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $client->post("https://api.payfast.co.za/subscriptions/{$token}/adhoc?testing=true",[
|
||||||
|
RequestOptions::JSON => ['body' => $body], RequestOptions::ALLOW_REDIRECTS => false
|
||||||
|
]);
|
||||||
|
|
||||||
|
return json_decode($response->getBody(),true);
|
||||||
|
}
|
||||||
|
catch(\Exception $e)
|
||||||
|
{
|
||||||
|
|
||||||
|
nlog($e->getMessage());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
202
app/PaymentDrivers/PayFastPaymentDriver.php
Normal file
202
app/PaymentDrivers/PayFastPaymentDriver.php
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\PaymentDrivers;
|
||||||
|
|
||||||
|
use App\Models\ClientGatewayToken;
|
||||||
|
use App\Models\GatewayType;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\PaymentHash;
|
||||||
|
use App\Models\SystemLog;
|
||||||
|
use App\PaymentDrivers\PayFast\CreditCard;
|
||||||
|
use App\PaymentDrivers\PayFast\Token;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class PayFastPaymentDriver extends BaseDriver
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
public $refundable = false; //does this gateway support refunds?
|
||||||
|
|
||||||
|
public $token_billing = false; //does this gateway support token billing?
|
||||||
|
|
||||||
|
public $can_authorise_credit_card = true; //does this gateway support authorizations?
|
||||||
|
|
||||||
|
public $payfast; //initialized gateway
|
||||||
|
|
||||||
|
public $payment_method; //initialized payment method
|
||||||
|
|
||||||
|
public static $methods = [
|
||||||
|
GatewayType::CREDIT_CARD => CreditCard::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYFAST;
|
||||||
|
|
||||||
|
//developer resources
|
||||||
|
//https://sandbox.payfast.co.za/
|
||||||
|
|
||||||
|
public function gatewayTypes(): array
|
||||||
|
{
|
||||||
|
$types = [];
|
||||||
|
|
||||||
|
if($this->client->currency()->code == 'ZAR')
|
||||||
|
$types[] = GatewayType::CREDIT_CARD;
|
||||||
|
|
||||||
|
return $types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endpointUrl()
|
||||||
|
{
|
||||||
|
if($this->company_gateway->getConfigField('testMode'))
|
||||||
|
return 'https://sandbox.payfast.co.za/eng/process';
|
||||||
|
|
||||||
|
return 'https://www.payfast.co.za/eng/process';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
|
$this->payfast = new \PayFast\PayFastPayment(
|
||||||
|
[
|
||||||
|
'merchantId' => $this->company_gateway->getConfigField('merchantId'),
|
||||||
|
'merchantKey' => $this->company_gateway->getConfigField('merchantKey'),
|
||||||
|
'passPhrase' => $this->company_gateway->getConfigField('passPhrase'),
|
||||||
|
'testMode' => $this->company_gateway->getConfigField('testMode')
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch(Exception $e) {
|
||||||
|
|
||||||
|
echo '##PAYFAST## There was an exception: '.$e->getMessage();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPaymentMethod($payment_method_id)
|
||||||
|
{
|
||||||
|
$class = self::$methods[$payment_method_id];
|
||||||
|
$this->payment_method = new $class($this);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorizeView(array $data)
|
||||||
|
{
|
||||||
|
return $this->payment_method->authorizeView($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorizeResponse($request)
|
||||||
|
{
|
||||||
|
return $this->payment_method->authorizeResponse($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processPaymentView(array $data)
|
||||||
|
{
|
||||||
|
return $this->payment_method->paymentView($data); //this is your custom implementation from here
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processPaymentResponse($request)
|
||||||
|
{
|
||||||
|
return $this->payment_method->paymentResponse($request); //this is your custom implementation from here
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refund(Payment $payment, $amount, $return_client_response = false)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||||
|
{
|
||||||
|
return (new Token($this))->tokenBilling($cgt, $payment_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateSignature($data)
|
||||||
|
{
|
||||||
|
$fields = array();
|
||||||
|
|
||||||
|
// specific order required by PayFast
|
||||||
|
// @see https://developers.payfast.co.za/documentation/#checkout-page
|
||||||
|
foreach (array('merchant_id', 'merchant_key', 'return_url', 'cancel_url', 'notify_url', 'name_first',
|
||||||
|
'name_last', 'email_address', 'cell_number',
|
||||||
|
/**
|
||||||
|
* Transaction Details
|
||||||
|
*/
|
||||||
|
'm_payment_id', 'amount', 'item_name', 'item_description',
|
||||||
|
/**
|
||||||
|
* Custom return data
|
||||||
|
*/
|
||||||
|
'custom_int1', 'custom_int2', 'custom_int3', 'custom_int4', 'custom_int5',
|
||||||
|
'custom_str1', 'custom_str2', 'custom_str3', 'custom_str4', 'custom_str5',
|
||||||
|
/**
|
||||||
|
* Email confirmation
|
||||||
|
*/
|
||||||
|
'email_confirmation', 'confirmation_address',
|
||||||
|
/**
|
||||||
|
* Payment Method
|
||||||
|
*/
|
||||||
|
'payment_method',
|
||||||
|
/**
|
||||||
|
* Subscriptions
|
||||||
|
*/
|
||||||
|
'subscription_type', 'billing_date', 'recurring_amount', 'frequency', 'cycles',
|
||||||
|
/**
|
||||||
|
* Passphrase for md5 signature generation
|
||||||
|
*/
|
||||||
|
'passphrase') as $key) {
|
||||||
|
if (!empty($data[$key])) {
|
||||||
|
$fields[$key] = $data[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return md5(http_build_query($fields));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function processWebhookRequest(Request $request, Payment $payment = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
$data = $request->all();
|
||||||
|
nlog($data);
|
||||||
|
|
||||||
|
if(array_key_exists('m_payment_id', $data))
|
||||||
|
{
|
||||||
|
|
||||||
|
$hash = Cache::get($data['m_payment_id']);
|
||||||
|
|
||||||
|
switch ($hash)
|
||||||
|
{
|
||||||
|
case 'cc_auth':
|
||||||
|
return $this->setPaymentMethod(GatewayType::CREDIT_CARD)
|
||||||
|
->authorizeResponse($request);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
$payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$data['m_payment_id']])->first();
|
||||||
|
|
||||||
|
return $this->setPaymentMethod(GatewayType::CREDIT_CARD)
|
||||||
|
->setPaymentHash($payment_hash)
|
||||||
|
->processPaymentResponse($request);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([], 200);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -114,7 +114,7 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
public function gatewayTypes(): array
|
public function gatewayTypes(): array
|
||||||
{
|
{
|
||||||
$types = [
|
$types = [
|
||||||
GatewayType::CRYPTO,
|
// GatewayType::CRYPTO,
|
||||||
GatewayType::CREDIT_CARD
|
GatewayType::CREDIT_CARD
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -117,7 +117,6 @@ use WePayCommon;
|
|||||||
nlog("authorize the card first!");
|
nlog("authorize the card first!");
|
||||||
|
|
||||||
$response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array(
|
$response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array(
|
||||||
// 'callback_uri' => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]),
|
|
||||||
'client_id' => config('ninja.wepay.client_id'),
|
'client_id' => config('ninja.wepay.client_id'),
|
||||||
'client_secret' => config('ninja.wepay.client_secret'),
|
'client_secret' => config('ninja.wepay.client_secret'),
|
||||||
'credit_card_id' => (int)$request->input('credit_card_id'),
|
'credit_card_id' => (int)$request->input('credit_card_id'),
|
||||||
|
@ -49,28 +49,11 @@ class CompanyRepository extends BaseRepository
|
|||||||
private function parseCustomFields($fields) :array
|
private function parseCustomFields($fields) :array
|
||||||
{
|
{
|
||||||
|
|
||||||
if(array_key_exists('account1', $fields))
|
|
||||||
$fields['company1'] = $fields['account1'];
|
|
||||||
|
|
||||||
if(array_key_exists('account2', $fields))
|
|
||||||
$fields['company2'] = $fields['account2'];
|
|
||||||
|
|
||||||
if(array_key_exists('invoice1', $fields))
|
|
||||||
$fields['surcharge1'] = $fields['invoice1'];
|
|
||||||
|
|
||||||
if(array_key_exists('invoice2', $fields))
|
|
||||||
$fields['surcharge2'] = $fields['invoice2'];
|
|
||||||
|
|
||||||
if(array_key_exists('invoice_text1', $fields))
|
|
||||||
$fields['invoice1'] = $fields['invoice_text1'];
|
|
||||||
|
|
||||||
if(array_key_exists('invoice_text2', $fields))
|
|
||||||
$fields['invoice2'] = $fields['invoice_text2'];
|
|
||||||
|
|
||||||
foreach ($fields as &$value) {
|
foreach ($fields as &$value) {
|
||||||
$value = (string) $value;
|
$value = (string) $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,21 +51,25 @@ class ClientService
|
|||||||
$credits = $this->client->credits()
|
$credits = $this->client->credits()
|
||||||
->where('is_deleted', false)
|
->where('is_deleted', false)
|
||||||
->where('balance', '>', 0)
|
->where('balance', '>', 0)
|
||||||
->whereDate('due_date', '<=', now()->format('Y-m-d'))
|
->where(function ($query){
|
||||||
->orWhere('due_date', NULL)
|
$query->whereDate('due_date', '<=', now()->format('Y-m-d'))
|
||||||
|
->orWhereNull('due_date');
|
||||||
|
})
|
||||||
->orderBy('created_at','ASC');
|
->orderBy('created_at','ASC');
|
||||||
|
|
||||||
return Number::roundValue($credits->sum('balance'), $this->client->currency()->precision);
|
return Number::roundValue($credits->sum('balance'), $this->client->currency()->precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCredits() :Collection
|
public function getCredits()
|
||||||
{
|
{
|
||||||
return $this->client->credits()
|
return $this->client->credits()
|
||||||
->where('is_deleted', false)
|
->where('is_deleted', false)
|
||||||
->where('balance', '>', 0)
|
->where('balance', '>', 0)
|
||||||
->whereDate('due_date', '<=', now()->format('Y-m-d'))
|
->where(function ($query){
|
||||||
->orWhere('due_date', NULL)
|
$query->whereDate('due_date', '<=', now()->format('Y-m-d'))
|
||||||
->orderBy('created_at','ASC');
|
->orWhereNull('due_date');
|
||||||
|
})
|
||||||
|
->orderBy('created_at','ASC')->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPaymentMethods(float $amount)
|
public function getPaymentMethods(float $amount)
|
||||||
|
@ -41,10 +41,13 @@ class GetCreditPdf extends AbstractService
|
|||||||
|
|
||||||
$file_path = $path.$this->credit->numberFormatter().'.pdf';
|
$file_path = $path.$this->credit->numberFormatter().'.pdf';
|
||||||
|
|
||||||
$disk = 'public';
|
// $disk = 'public';
|
||||||
|
$disk = config('filesystems.default');
|
||||||
|
|
||||||
$file_path = CreateEntityPdf::dispatchNow($this->invitation);
|
$file_path = CreateEntityPdf::dispatchNow($this->invitation);
|
||||||
|
|
||||||
return Storage::disk($disk)->path($file_path);
|
nlog($file_path);
|
||||||
|
return $file_path;
|
||||||
|
// return Storage::disk($disk)->path($file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,9 +49,9 @@ class GenerateDeliveryNote
|
|||||||
|
|
||||||
$this->contact = $contact;
|
$this->contact = $contact;
|
||||||
|
|
||||||
$this->disk = 'public';
|
// $this->disk = 'public';
|
||||||
|
|
||||||
// $this->disk = $disk ?? config('filesystems.default');
|
$this->disk = $disk ?? config('filesystems.default');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run()
|
public function run()
|
||||||
@ -111,7 +111,8 @@ class GenerateDeliveryNote
|
|||||||
|
|
||||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||||
|
|
||||||
return Storage::disk($this->disk)->path($file_path);
|
//return Storage::disk($this->disk)->path($file_path);
|
||||||
|
|
||||||
|
return $file_path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,8 @@ class GetInvoicePdf extends AbstractService
|
|||||||
|
|
||||||
$file_path = $path.$this->invoice->numberFormatter().'.pdf';
|
$file_path = $path.$this->invoice->numberFormatter().'.pdf';
|
||||||
|
|
||||||
$disk = 'public';
|
// $disk = 'public';
|
||||||
|
$disk = config('filesystems.default');
|
||||||
|
|
||||||
$file = Storage::disk($disk)->exists($file_path);
|
$file = Storage::disk($disk)->exists($file_path);
|
||||||
|
|
||||||
@ -47,6 +48,8 @@ class GetInvoicePdf extends AbstractService
|
|||||||
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Storage::disk($disk)->path($file_path);
|
// return Storage::disk($disk)->path($file_path);
|
||||||
|
//
|
||||||
|
return $file_path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,8 @@ class HandleReversal extends AbstractService
|
|||||||
$credit = CreditFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
$credit = CreditFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||||
$credit->client_id = $this->invoice->client_id;
|
$credit->client_id = $this->invoice->client_id;
|
||||||
$credit->invoice_id = $this->invoice->id;
|
$credit->invoice_id = $this->invoice->id;
|
||||||
|
$credit->date = now();
|
||||||
|
|
||||||
$item = InvoiceItemFactory::create();
|
$item = InvoiceItemFactory::create();
|
||||||
$item->quantity = 1;
|
$item->quantity = 1;
|
||||||
$item->cost = (float) $total_paid;
|
$item->cost = (float) $total_paid;
|
||||||
|
@ -119,6 +119,7 @@ class PaymentService
|
|||||||
->service()
|
->service()
|
||||||
->getCredits();
|
->getCredits();
|
||||||
|
|
||||||
|
|
||||||
foreach ($credits as $credit) {
|
foreach ($credits as $credit) {
|
||||||
//starting invoice balance
|
//starting invoice balance
|
||||||
$invoice_balance = $invoice->balance;
|
$invoice_balance = $invoice->balance;
|
||||||
|
@ -39,10 +39,12 @@ class GetQuotePdf extends AbstractService
|
|||||||
|
|
||||||
$file_path = $path.$this->quote->numberFormatter().'.pdf';
|
$file_path = $path.$this->quote->numberFormatter().'.pdf';
|
||||||
|
|
||||||
$disk = 'public';
|
// $disk = 'public';
|
||||||
|
$disk = config('filesystems.default');
|
||||||
|
|
||||||
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
||||||
|
|
||||||
return Storage::disk($disk)->path($file_path);
|
return $file_path;
|
||||||
|
//return Storage::disk($disk)->path($file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,11 @@ class QuoteService
|
|||||||
|
|
||||||
$this->quote->fresh();
|
$this->quote->fresh();
|
||||||
|
|
||||||
|
if ($this->quote->client->getSetting('auto_archive_quote')) {
|
||||||
|
$quote_repo = new QuoteRepository();
|
||||||
|
$quote_repo->archive($this->quote);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,10 +134,6 @@ class QuoteService
|
|||||||
public function convertToInvoice()
|
public function convertToInvoice()
|
||||||
{
|
{
|
||||||
|
|
||||||
//to prevent circular references we need to explicit call this here.
|
|
||||||
// $mark_approved = new MarkApproved($this->quote->client);
|
|
||||||
// $this->quote = $mark_approved->run($this->quote);
|
|
||||||
|
|
||||||
$this->convert();
|
$this->convert();
|
||||||
|
|
||||||
$this->invoice->service()->createInvitations();
|
$this->invoice->service()->createInvitations();
|
||||||
|
@ -41,14 +41,14 @@ class GetInvoicePdf extends AbstractService
|
|||||||
|
|
||||||
$file_path = $path.$this->entity->hashed_id.'.pdf';
|
$file_path = $path.$this->entity->hashed_id.'.pdf';
|
||||||
|
|
||||||
$disk = 'public';
|
$disk = config('filesystems.default');
|
||||||
|
|
||||||
$file = Storage::disk($disk)->exists($file_path);
|
$file = Storage::disk($disk)->exists($file_path);
|
||||||
|
|
||||||
if (! $file) {
|
if (! $file) {
|
||||||
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Storage::disk($disk)->path($file_path);
|
return $file_path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,8 @@ class SubscriptionService
|
|||||||
$recurring_invoice->next_send_date = now();
|
$recurring_invoice->next_send_date = now();
|
||||||
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
|
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
|
||||||
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
|
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
|
||||||
|
$recurring_invoice->auto_bill = $this->subscription->auto_bill;
|
||||||
|
|
||||||
/* Start the recurring service */
|
/* Start the recurring service */
|
||||||
$recurring_invoice->service()
|
$recurring_invoice->service()
|
||||||
->start()
|
->start()
|
||||||
|
@ -26,9 +26,9 @@ use App\Utils\CurlUtils;
|
|||||||
use App\Utils\HtmlEngine;
|
use App\Utils\HtmlEngine;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Illuminate\Support\Facades\Response;
|
use Illuminate\Support\Facades\Response;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Phantom
|
class Phantom
|
||||||
|
@ -48,7 +48,7 @@ trait AppSetup
|
|||||||
$orderBy = 'num_days';
|
$orderBy = 'num_days';
|
||||||
} elseif ($name == 'fonts') {
|
} elseif ($name == 'fonts') {
|
||||||
$orderBy = 'sort_order';
|
$orderBy = 'sort_order';
|
||||||
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
|
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks', 'timezones'])) {
|
||||||
$orderBy = 'name';
|
$orderBy = 'name';
|
||||||
} else {
|
} else {
|
||||||
$orderBy = 'id';
|
$orderBy = 'id';
|
||||||
|
@ -34,6 +34,11 @@ trait PdfMaker
|
|||||||
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
|
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config('ninja.snappdf_chromium_arguments')) {
|
||||||
|
$pdf->clearChromiumArguments();
|
||||||
|
$pdf->addChromiumArguments(config('ninja.snappdf_chromium_arguments'));
|
||||||
|
}
|
||||||
|
|
||||||
$generated = $pdf
|
$generated = $pdf
|
||||||
->setHtml($html)
|
->setHtml($html)
|
||||||
->generate();
|
->generate();
|
||||||
|
@ -35,8 +35,15 @@ trait SubscriptionHooker
|
|||||||
'headers' => $headers,
|
'headers' => $headers,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
nlog("method name must be a string");
|
||||||
|
nlog($subscription->webhook_configuration['post_purchase_rest_method']);
|
||||||
|
nlog($subscription->webhook_configuration['post_purchase_url']);
|
||||||
|
|
||||||
|
$post_purchase_rest_method = (string)$subscription->webhook_configuration['post_purchase_rest_method'];
|
||||||
|
$post_purchase_url = (string)$subscription->webhook_configuration['post_purchase_url'];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$response = $client->{$subscription->webhook_configuration['post_purchase_rest_method']}($subscription->webhook_configuration['post_purchase_url'],[
|
$response = $client->{$post_purchase_rest_method}($post_purchase_url,[
|
||||||
RequestOptions::JSON => ['body' => $body], RequestOptions::ALLOW_REDIRECTS => false
|
RequestOptions::JSON => ['body' => $body], RequestOptions::ALLOW_REDIRECTS => false
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
"asm/php-ansible": "dev-main",
|
"asm/php-ansible": "dev-main",
|
||||||
"authorizenet/authorizenet": "^2.0",
|
"authorizenet/authorizenet": "^2.0",
|
||||||
"bacon/bacon-qr-code": "^2.0",
|
"bacon/bacon-qr-code": "^2.0",
|
||||||
"beganovich/snappdf": "^1.0",
|
"beganovich/snappdf": "^1.7",
|
||||||
"braintree/braintree_php": "^6.0",
|
"braintree/braintree_php": "^6.0",
|
||||||
"checkout/checkout-sdk-php": "^1.0",
|
"checkout/checkout-sdk-php": "^1.0",
|
||||||
"cleverit/ubl_invoice": "^1.3",
|
"cleverit/ubl_invoice": "^1.3",
|
||||||
@ -47,6 +47,7 @@
|
|||||||
"google/apiclient": "^2.7",
|
"google/apiclient": "^2.7",
|
||||||
"guzzlehttp/guzzle": "^7.0.1",
|
"guzzlehttp/guzzle": "^7.0.1",
|
||||||
"hashids/hashids": "^4.0",
|
"hashids/hashids": "^4.0",
|
||||||
|
"hedii/laravel-gelf-logger": "^6.0",
|
||||||
"intervention/image": "^2.5",
|
"intervention/image": "^2.5",
|
||||||
"laracasts/presenter": "^0.2.1",
|
"laracasts/presenter": "^0.2.1",
|
||||||
"laravel/framework": "^8.0",
|
"laravel/framework": "^8.0",
|
||||||
@ -63,6 +64,7 @@
|
|||||||
"maennchen/zipstream-php": "^1.2",
|
"maennchen/zipstream-php": "^1.2",
|
||||||
"nwidart/laravel-modules": "^8.0",
|
"nwidart/laravel-modules": "^8.0",
|
||||||
"omnipay/paypal": "^3.0",
|
"omnipay/paypal": "^3.0",
|
||||||
|
"payfast/payfast-php-sdk": "^1.1",
|
||||||
"pragmarx/google2fa": "^8.0",
|
"pragmarx/google2fa": "^8.0",
|
||||||
"predis/predis": "^1.1",
|
"predis/predis": "^1.1",
|
||||||
"sentry/sentry-laravel": "^2",
|
"sentry/sentry-laravel": "^2",
|
||||||
|
772
composer.lock
generated
772
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -102,8 +102,71 @@ return [
|
|||||||
|
|
||||||
'invoiceninja' => [
|
'invoiceninja' => [
|
||||||
'driver' => 'single',
|
'driver' => 'single',
|
||||||
|
'level' => 'debug',
|
||||||
'path' => storage_path('logs/invoiceninja.log'),
|
'path' => storage_path('logs/invoiceninja.log'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'gelf' => [
|
||||||
|
'driver' => 'custom',
|
||||||
|
|
||||||
|
'via' => \Hedii\LaravelGelfLogger\GelfLoggerFactory::class,
|
||||||
|
|
||||||
|
// This optional option determines the processors that should be
|
||||||
|
// pushed to the handler. This option is useful to modify a field
|
||||||
|
// in the log context (see NullStringProcessor), or to add extra
|
||||||
|
// data. Each processor must be a callable or an object with an
|
||||||
|
// __invoke method: see monolog documentation about processors.
|
||||||
|
// Default is an empty array.
|
||||||
|
'processors' => [
|
||||||
|
\Hedii\LaravelGelfLogger\Processors\NullStringProcessor::class,
|
||||||
|
// another processor...
|
||||||
|
],
|
||||||
|
|
||||||
|
// This optional option determines the minimum "level" a message
|
||||||
|
// must be in order to be logged by the channel. Default is 'debug'
|
||||||
|
'level' => 'debug',
|
||||||
|
|
||||||
|
// This optional option determines the channel name sent with the
|
||||||
|
// message in the 'facility' field. Default is equal to app.env
|
||||||
|
// configuration value
|
||||||
|
'name' => 'my-custom-name',
|
||||||
|
|
||||||
|
// This optional option determines the system name sent with the
|
||||||
|
// message in the 'source' field. When forgotten or set to null,
|
||||||
|
// the current hostname is used.
|
||||||
|
'system_name' => null,
|
||||||
|
|
||||||
|
// This optional option determines if you want the UDP, TCP or HTTP
|
||||||
|
// transport for the gelf log messages. Default is UDP
|
||||||
|
'transport' => 'udp',
|
||||||
|
|
||||||
|
// This optional option determines the host that will receive the
|
||||||
|
// gelf log messages. Default is 127.0.0.1
|
||||||
|
'host' => env('GRAYLOG_SERVER', '127.0.0.1'),
|
||||||
|
|
||||||
|
// This optional option determines the port on which the gelf
|
||||||
|
// receiver host is listening. Default is 12201
|
||||||
|
'port' => 12201,
|
||||||
|
|
||||||
|
// This optional option determines the path used for the HTTP
|
||||||
|
// transport. When forgotten or set to null, default path '/gelf'
|
||||||
|
// is used.
|
||||||
|
'path' => null,
|
||||||
|
|
||||||
|
// This optional option determines the maximum length per message
|
||||||
|
// field. When forgotten or set to null, the default value of
|
||||||
|
// \Monolog\Formatter\GelfMessageFormatter::DEFAULT_MAX_LENGTH is
|
||||||
|
// used (currently this value is 32766)
|
||||||
|
'max_length' => null,
|
||||||
|
|
||||||
|
// This optional option determines the prefix for 'context' fields
|
||||||
|
// from the Monolog record. Default is null (no context prefix)
|
||||||
|
'context_prefix' => null,
|
||||||
|
|
||||||
|
// This optional option determines the prefix for 'extra' fields
|
||||||
|
// from the Monolog record. Default is null (no extra prefix)
|
||||||
|
'extra_prefix' => null,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
@ -14,8 +14,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => '5.2.8',
|
'app_version' => '5.2.11',
|
||||||
'app_tag' => '5.2.8',
|
'app_tag' => '5.2.11',
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', ''),
|
'api_secret' => env('API_SECRET', ''),
|
||||||
@ -144,6 +144,7 @@ return [
|
|||||||
'log_pdf_html' => env('LOG_PDF_HTML', false),
|
'log_pdf_html' => env('LOG_PDF_HTML', false),
|
||||||
'expanded_logging' => env('EXPANDED_LOGGING', false),
|
'expanded_logging' => env('EXPANDED_LOGGING', false),
|
||||||
'snappdf_chromium_path' => env('SNAPPDF_CHROMIUM_PATH', false),
|
'snappdf_chromium_path' => env('SNAPPDF_CHROMIUM_PATH', false),
|
||||||
|
'snappdf_chromium_arguments' => env('SNAPPDF_CHROMIUM_ARGUMENTS', false),
|
||||||
'v4_migration_version' => '4.5.35',
|
'v4_migration_version' => '4.5.35',
|
||||||
'flutter_renderer' => env('FLUTTER_RENDERER', 'selfhosted-html'),
|
'flutter_renderer' => env('FLUTTER_RENDERER', 'selfhosted-html'),
|
||||||
'webcron_secret' => env('WEBCRON_SECRET', false),
|
'webcron_secret' => env('WEBCRON_SECRET', false),
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
|
|
||||||
class UpdateDesigns extends Migration
|
class UpdateDesigns extends Migration
|
||||||
{
|
{
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Gateway;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class ActivatePayfastPaymentDriver extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Gateway::whereIn('id', [11])->update(['visible' => 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
4
public/flutter_service_worker.js
vendored
4
public/flutter_service_worker.js
vendored
@ -4,7 +4,7 @@ const TEMP = 'flutter-temp-cache';
|
|||||||
const CACHE_NAME = 'flutter-app-cache';
|
const CACHE_NAME = 'flutter-app-cache';
|
||||||
const RESOURCES = {
|
const RESOURCES = {
|
||||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||||
"main.dart.js": "ec0d23274ffa0ea4b6743fe88c16ccaa",
|
"main.dart.js": "3b486fb6ff9ff8c688f498feb859535a",
|
||||||
"/": "23224b5e03519aaa87594403d54412cf",
|
"/": "23224b5e03519aaa87594403d54412cf",
|
||||||
"manifest.json": "ce1b79950eb917ea619a0a30da27c6a3",
|
"manifest.json": "ce1b79950eb917ea619a0a30da27c6a3",
|
||||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||||
@ -29,7 +29,7 @@ const RESOURCES = {
|
|||||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||||
"assets/AssetManifest.json": "7e49562f32e24a9e2557fe4178a84b79",
|
"assets/AssetManifest.json": "7e49562f32e24a9e2557fe4178a84b79",
|
||||||
"version.json": "4d10e2258012cbb88b24009334a24f24",
|
"version.json": "d5a91f8fb6852f3ca9a9199e65eb6dbd",
|
||||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||||
"favicon.ico": "51636d3a390451561744c42188ccd628"
|
"favicon.ico": "51636d3a390451561744c42188ccd628"
|
||||||
|
175643
public/main.dart.js
vendored
175643
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
176083
public/main.foss.dart.js
vendored
176083
public/main.foss.dart.js
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user