mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
commit
2b6ef1e30a
@ -53,5 +53,8 @@ PHANTOMJS_SECRET=secret
|
||||
|
||||
UPDATE_SECRET=secret
|
||||
|
||||
DELETE_PDF_DAYS=60
|
||||
DELETE_BACKUP_DAYS=60
|
||||
|
||||
COMPOSER_AUTH='{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}'
|
||||
SENTRY_LARAVEL_DSN=https://39389664f3f14969b4c43dadda00a40b@sentry2.invoicing.co/5
|
@ -1 +1 @@
|
||||
5.3.89
|
||||
5.3.90
|
@ -19,6 +19,7 @@ use App\Jobs\Ledger\LedgerBalanceUpdate;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
use App\Jobs\Ninja\CompanySizeCheck;
|
||||
use App\Jobs\Ninja\QueueSize;
|
||||
use App\Jobs\Ninja\SystemMaintenance;
|
||||
use App\Jobs\Util\DiskCleanup;
|
||||
use App\Jobs\Util\ReminderJob;
|
||||
use App\Jobs\Util\SchedulerCheck;
|
||||
@ -56,7 +57,6 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping();
|
||||
|
||||
// $schedule->job(new LedgerBalanceUpdate)->everyFiveMinutes()->withoutOverlapping();
|
||||
$schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new CompanySizeCheck)->daily()->withoutOverlapping();
|
||||
@ -73,6 +73,8 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new SchedulerCheck)->daily()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new SystemMaintenance)->weekly()->withoutOverlapping();
|
||||
|
||||
if(Ninja::isSelfHost())
|
||||
{
|
||||
|
||||
|
@ -138,6 +138,27 @@ class InvoiceFilters extends QueryFilters
|
||||
});
|
||||
}
|
||||
|
||||
public function upcoming()
|
||||
{
|
||||
return $this->builder
|
||||
->where(function ($query) {
|
||||
$query->whereNull('due_date')
|
||||
->orWhere('due_date', '>', now());
|
||||
})
|
||||
->orderBy('due_date', 'ASC');
|
||||
}
|
||||
|
||||
public function overdue()
|
||||
{
|
||||
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('is_deleted', 0)
|
||||
->where(function ($query) {
|
||||
$query->where('due_date', '<', now())
|
||||
->orWhere('partial_due_date', '<', now());
|
||||
})
|
||||
->orderBy('due_date', 'ASC');
|
||||
}
|
||||
|
||||
public function payable(string $client_id)
|
||||
{
|
||||
if (strlen($client_id) == 0) {
|
||||
|
@ -392,7 +392,7 @@ class LoginController extends BaseController
|
||||
if($cu->count() == 0)
|
||||
return $cu;
|
||||
|
||||
if(auth()->user()->company_users()->count() != auth()->user()->tokens()->count())
|
||||
if(auth()->user()->company_users()->count() != auth()->user()->tokens()->distinct('company_id')->count())
|
||||
{
|
||||
|
||||
auth()->user()->companies->each(function($company){
|
||||
@ -435,49 +435,11 @@ class LoginController extends BaseController
|
||||
|
||||
Auth::login($existing_user, true);
|
||||
|
||||
// $cu = CompanyUser::query()
|
||||
// ->where('user_id', auth()->user()->id)
|
||||
// ->where('company_id', $existing_user->account->default_company_id);
|
||||
|
||||
// if($cu->exists())
|
||||
// $set_company = $existing_user->account->default_company;
|
||||
// else{
|
||||
// $cu = CompanyUser::query()->where('user_id', auth()->user()->id);
|
||||
// $set_company = $cu->company;
|
||||
// }
|
||||
|
||||
// $existing_user->setCompany($set_company);
|
||||
|
||||
// $this->setLoginCache($existing_user);
|
||||
|
||||
$cu = $this->hydrateCompanyUser();
|
||||
|
||||
if($cu->count() == 0)
|
||||
return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
|
||||
|
||||
// $truth = app()->make(TruthSource::class);
|
||||
// $truth->setCompanyUser($cu->first());
|
||||
// $truth->setUser($existing_user);
|
||||
// $truth->setCompany($set_company);
|
||||
|
||||
// if($existing_user->company_users()->count() != $existing_user->tokens()->count())
|
||||
// {
|
||||
|
||||
// $existing_user->companies->each(function($company) use($existing_user){
|
||||
|
||||
// if(!CompanyToken::where('user_id', $existing_user->id)->where('company_id', $company->id)->exists()){
|
||||
|
||||
// CreateCompanyToken::dispatchNow($company, $existing_user, "Google_O_Auth");
|
||||
|
||||
// }
|
||||
|
||||
// });
|
||||
|
||||
// }
|
||||
|
||||
// $truth->setCompanyToken(CompanyToken::where('user_id', $existing_user->id)->where('company_id', $set_company->id)->first());
|
||||
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
@ -493,9 +455,6 @@ class LoginController extends BaseController
|
||||
|
||||
Auth::login($existing_login_user, true);
|
||||
|
||||
// $existing_login_user->setCompany($existing_login_user->account->default_company);
|
||||
// $this->setLoginCache($existing_login_user);
|
||||
|
||||
auth()->user()->update([
|
||||
'oauth_user_id' => $google->harvestSubField($user),
|
||||
'oauth_provider_id'=> 'google',
|
||||
@ -503,35 +462,9 @@ class LoginController extends BaseController
|
||||
|
||||
$cu = $this->hydrateCompanyUser();
|
||||
|
||||
// $cu = CompanyUser::query()
|
||||
// ->where('user_id', auth()->user()->id);
|
||||
|
||||
if($cu->count() == 0)
|
||||
return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
|
||||
|
||||
// $truth = app()->make(TruthSource::class);
|
||||
// $truth->setCompanyUser($cu->first());
|
||||
// $truth->setUser($existing_login_user);
|
||||
// $truth->setCompany($existing_login_user->account->default_company);
|
||||
|
||||
|
||||
// if($existing_login_user->company_users()->count() != $existing_login_user->tokens()->count())
|
||||
// {
|
||||
|
||||
// $existing_login_user->companies->each(function($company) use($existing_login_user){
|
||||
|
||||
// if(!CompanyToken::where('user_id', $existing_login_user->id)->where('company_id', $company->id)->exists()){
|
||||
|
||||
// CreateCompanyToken::dispatchNow($company, $existing_login_user, "Google_O_Auth");
|
||||
|
||||
// }
|
||||
|
||||
// });
|
||||
|
||||
// }
|
||||
|
||||
// $truth->setCompanyToken(CompanyToken::where('user_id', $existing_login_user->id)->where('company_id', $existing_login_user->account->default_company->id)->first());
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
@ -551,9 +484,6 @@ class LoginController extends BaseController
|
||||
|
||||
Auth::login($existing_login_user, true);
|
||||
|
||||
// $existing_login_user->setCompany($existing_login_user->account->default_company);
|
||||
// $this->setLoginCache($existing_login_user);
|
||||
|
||||
auth()->user()->update([
|
||||
'oauth_user_id' => $google->harvestSubField($user),
|
||||
'oauth_provider_id'=> 'google',
|
||||
@ -567,29 +497,6 @@ class LoginController extends BaseController
|
||||
if($cu->count() == 0)
|
||||
return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
|
||||
|
||||
// $truth = app()->make(TruthSource::class);
|
||||
// $truth->setCompanyUser($cu->first());
|
||||
// $truth->setUser($existing_login_user);
|
||||
// $truth->setCompany($existing_login_user->account->default_company);
|
||||
|
||||
|
||||
// if($existing_login_user->company_users()->count() != $existing_login_user->tokens()->count())
|
||||
// {
|
||||
|
||||
// $existing_login_user->companies->each(function($company) use($existing_login_user){
|
||||
|
||||
// if(!CompanyToken::where('user_id', $existing_login_user->id)->where('company_id', $company->id)->exists()){
|
||||
|
||||
// CreateCompanyToken::dispatchNow($company, $existing_login_user, "Google_O_Auth");
|
||||
|
||||
// }
|
||||
|
||||
// });
|
||||
|
||||
// }
|
||||
|
||||
// $truth->setCompanyToken(CompanyToken::where('user_id', $existing_login_user->id)->where('company_id', $existing_login_user->account->default_company->id)->first());
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
@ -617,38 +524,11 @@ class LoginController extends BaseController
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->save();
|
||||
|
||||
// auth()->user()->setCompany(auth()->user()->account->default_company);
|
||||
// $this->setLoginCache(auth()->user());
|
||||
// $cu = CompanyUser::whereUserId(auth()->user()->id);
|
||||
|
||||
$cu = $this->hydrateCompanyUser();
|
||||
|
||||
if($cu->count() == 0)
|
||||
return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
|
||||
|
||||
// $truth = app()->make(TruthSource::class);
|
||||
// $truth->setCompanyUser($cu->first());
|
||||
// $truth->setUser(auth()->user());
|
||||
// $truth->setCompany(auth()->user()->account->default_company);
|
||||
|
||||
// if(auth()->user()->company_users()->count() != auth()->user()->tokens()->count())
|
||||
// {
|
||||
|
||||
// auth()->user()->companies->each(function($company) {
|
||||
|
||||
// if(!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->exists()){
|
||||
|
||||
// CreateCompanyToken::dispatchNow($company, auth()->user(), "Google_O_Auth");
|
||||
|
||||
// }
|
||||
|
||||
// });
|
||||
|
||||
// }
|
||||
|
||||
// $truth->setCompanyToken(CompanyToken::where('user_id', auth()->user()->id)->where('company_id', auth()->user()->account->default_company->id)->first());
|
||||
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
|
@ -59,13 +59,6 @@ class NinjaPlanController extends Controller
|
||||
|
||||
Auth::guard('contact')->loginUsingId($client_contact->id,true);
|
||||
|
||||
// /* Current paid users get pushed straight to subscription overview page*/
|
||||
// if($account->isPaidHostedClient())
|
||||
// return redirect('/client/dashboard');
|
||||
|
||||
// /* Users that are not paid get pushed to a custom purchase page */
|
||||
// return $this->render('subscriptions.ninja_plan', ['settings' => $client_contact->company->settings]);
|
||||
|
||||
return $this->plan();
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\ContactExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -62,8 +63,11 @@ class ClientContactReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), ContactExport::class, $this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new ContactExport(auth()->user()->company(), $request->all());
|
||||
|
||||
$csv = $export->run();
|
||||
@ -80,5 +84,4 @@ class ClientContactReportController extends BaseController
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\ClientExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Models\Client;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
@ -63,6 +64,10 @@ class ClientReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),ClientExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new ClientExport(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\CreditExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -62,6 +63,10 @@ class CreditReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),CreditExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new CreditExport(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\DocumentExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -62,6 +63,10 @@ class DocumentReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),DocumentExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new DocumentExport(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\ExpenseExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Models\Client;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
@ -63,6 +64,10 @@ class ExpenseReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),ExpenseExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new ExpenseExport(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\InvoiceItemExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -62,6 +63,10 @@ class InvoiceItemReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),InvoiceItemExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new InvoiceItemExport(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\InvoiceExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -62,6 +63,10 @@ class InvoiceReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),InvoiceExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new InvoiceExport(auth()->user()->company(), $request->all());
|
||||
@ -80,5 +85,4 @@ class InvoiceReportController extends BaseController
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\PaymentExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Models\Client;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
@ -63,6 +64,10 @@ class PaymentReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(), $request->all(), PaymentExport::class, $this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new PaymentExport(auth()->user()->company(), $request->all());
|
||||
@ -81,5 +86,4 @@ class PaymentReportController extends BaseController
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\ProductExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Models\Client;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
@ -63,6 +64,10 @@ class ProductReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),ProductExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new ProductExport(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\PaymentExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\ProfitLossRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Models\Client;
|
||||
use App\Services\Report\ProfitLoss;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
@ -64,6 +65,10 @@ class ProfitAndLossController extends BaseController
|
||||
*/
|
||||
public function __invoke(ProfitLossRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),ProfitLoss::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$pnl = new ProfitLoss(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\QuoteItemExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -62,6 +63,10 @@ class QuoteItemReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),QuoteItemExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new QuoteItemExport(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\QuoteExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -62,6 +63,10 @@ class QuoteReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),QuoteExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new QuoteExport(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\RecurringInvoiceExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -62,6 +63,10 @@ class RecurringInvoiceReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),RecurringInvoiceExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new RecurringInvoiceExport(auth()->user()->company(), $request->all());
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
|
||||
use App\Export\CSV\TaskExport;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Report\SendToAdmin;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -62,6 +63,10 @@ class TaskReportController extends BaseController
|
||||
*/
|
||||
public function __invoke(GenericReportRequest $request)
|
||||
{
|
||||
if ($request->has('send_email') && $request->get('send_email')) {
|
||||
SendToAdmin::dispatch(auth()->user()->company(),$request->all(),TaskExport::class,$this->filename);
|
||||
return response()->json(['message' => 'working...'], 200);
|
||||
}
|
||||
// expect a list of visible fields, or use the default
|
||||
|
||||
$export = new TaskExport(auth()->user()->company(), $request->all());
|
||||
|
@ -41,7 +41,7 @@ class PaymentsTable extends Component
|
||||
{
|
||||
$query = Payment::query()
|
||||
->with('type', 'client')
|
||||
->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_REFUNDED, Payment::STATUS_PARTIALLY_REFUNDED])
|
||||
->whereIn('status_id', [Payment::STATUS_FAILED, Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_REFUNDED, Payment::STATUS_PARTIALLY_REFUNDED])
|
||||
->where('company_id', $this->company->id)
|
||||
->where('client_id', auth()->guard('contact')->user()->client->id)
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
|
@ -79,7 +79,9 @@ class StoreInvoiceRequest extends Request
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
if (isset($input['line_items']) && is_array($input['line_items']))
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
|
||||
$input['amount'] = 0;
|
||||
$input['balance'] = 0;
|
||||
|
||||
|
@ -76,7 +76,7 @@ class UpdateInvoiceRequest extends Request
|
||||
|
||||
$input['id'] = $this->invoice->id;
|
||||
|
||||
if (isset($input['line_items'])) {
|
||||
if (isset($input['line_items']) && is_array($input['line_items'])) {
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ class ClientLedgerBalanceUpdate implements ShouldQueue
|
||||
*/
|
||||
public function handle() :void
|
||||
{
|
||||
nlog("Updating company ledgers");
|
||||
nlog("Updating company ledger for client ". $this->client->id);
|
||||
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
@ -62,8 +62,14 @@ class ClientLedgerBalanceUpdate implements ShouldQueue
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
|
||||
if(!$last_record)
|
||||
return;
|
||||
if(!$last_record){
|
||||
|
||||
$last_record = CompanyLedger::where('client_id', $company_ledger->client_id)
|
||||
->where('company_id', $company_ledger->company_id)
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
|
||||
}
|
||||
|
||||
nlog("Updating Balance NOW");
|
||||
|
||||
@ -72,13 +78,7 @@ class ClientLedgerBalanceUpdate implements ShouldQueue
|
||||
|
||||
});
|
||||
|
||||
nlog("Finished checking company ledgers");
|
||||
|
||||
}
|
||||
|
||||
public function checkLedger()
|
||||
{
|
||||
|
||||
nlog("Updating company ledger for client ". $this->client->id);
|
||||
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,6 @@ class PaymentFailedMailer implements ShouldQueue
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
//Set DB
|
||||
MultiDB::setDb($this->company->db);
|
||||
App::setLocale($this->client->locale());
|
||||
|
132
app/Jobs/Ninja/SystemMaintenance.php
Normal file
132
app/Jobs/Ninja/SystemMaintenance.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Ninja;
|
||||
|
||||
use App\Models\Backup;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class SystemMaintenance implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
nlog("Starting System Maintenance");
|
||||
|
||||
// if(Ninja::isHosted())
|
||||
// return;
|
||||
|
||||
$delete_pdf_days = config('ninja.maintenance.delete_pdfs');
|
||||
|
||||
nlog("Number of days to keep PDFs {$delete_pdf_days}");
|
||||
|
||||
$delete_backup_days = config('ninja.maintenance.delete_backups');
|
||||
|
||||
nlog("Number of days to keep PDFs {$delete_backup_days}");
|
||||
|
||||
$this->maintainPdfs($delete_pdf_days);
|
||||
|
||||
$this->maintainBackups($delete_backup_days);
|
||||
|
||||
}
|
||||
|
||||
private function maintainPdfs(int $delete_pdf_days)
|
||||
{
|
||||
if($delete_pdf_days == 0)
|
||||
return;
|
||||
|
||||
Invoice::with('invitations')
|
||||
->whereBetween('created_at', [now()->subYear(), now()->subDays($delete_pdf_days)])
|
||||
->withTrashed()
|
||||
->cursor()
|
||||
->each(function ($invoice){
|
||||
|
||||
nlog("deleting invoice {$invoice->number}");
|
||||
|
||||
$invoice->service()->deletePdf();
|
||||
|
||||
});
|
||||
|
||||
Quote::with('invitations')
|
||||
->whereBetween('created_at', [now()->subYear(), now()->subDays($delete_pdf_days)])
|
||||
->withTrashed()
|
||||
->cursor()
|
||||
->each(function ($quote){
|
||||
|
||||
nlog("deleting quote {$quote->number}");
|
||||
|
||||
$quote->service()->deletePdf();
|
||||
|
||||
});
|
||||
|
||||
Credit::with('invitations')
|
||||
->whereBetween('created_at', [now()->subYear(), now()->subDays($delete_pdf_days)])
|
||||
->withTrashed()
|
||||
->cursor()
|
||||
->each(function ($credit){
|
||||
|
||||
nlog("deleting credit {$credit->number}");
|
||||
|
||||
$credit->service()->deletePdf();
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function maintainBackups(int $delete_backup_days)
|
||||
{
|
||||
if($delete_backup_days == 0)
|
||||
return;
|
||||
|
||||
Backup::where('created_at', '<', now()->subDays($delete_backup_days))
|
||||
->cursor()
|
||||
->each(function ($backup){
|
||||
|
||||
nlog("deleting {$backup->filename}");
|
||||
|
||||
if($backup->filename)
|
||||
$backup->deleteFile();
|
||||
|
||||
$backup->delete();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
64
app/Jobs/Report/SendToAdmin.php
Normal file
64
app/Jobs/Report/SendToAdmin.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Report;
|
||||
|
||||
|
||||
use App\Http\Requests\Report\GenericReportRequest;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\DownloadReport;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class SendToAdmin implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected Company $company;
|
||||
protected array $request;
|
||||
protected string $report_class;
|
||||
protected string $file_name;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
*/
|
||||
public function __construct(Company $company, array $request, $report_class, $file_name)
|
||||
{
|
||||
$this->company = $company;
|
||||
$this->request = $request;
|
||||
$this->report_class = $report_class;
|
||||
$this->file_name = $file_name;
|
||||
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
$export = new $this->report_class($this->company, $this->request);
|
||||
$csv = $export->run();
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadReport($this->company, $csv, $this->file_name);
|
||||
$nmo->company = $this->company;
|
||||
$nmo->settings = $this->company->settings;
|
||||
$nmo->to_user = $this->company->owner();
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
}
|
||||
}
|
@ -237,7 +237,6 @@ class Import implements ShouldQueue
|
||||
|
||||
//company size check
|
||||
if ($this->company->invoices()->count() > 500 || $this->company->products()->count() > 500 || $this->company->clients()->count() > 500) {
|
||||
// $this->company->is_large = true;
|
||||
$this->company->account->companies()->update(['is_large' => true]);
|
||||
}
|
||||
|
||||
|
62
app/Mail/DownloadReport.php
Normal file
62
app/Mail/DownloadReport.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Company;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class DownloadReport extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
protected Company $company;
|
||||
protected $csv;
|
||||
protected string $file_name;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Company $company, $csv, $file_name)
|
||||
{
|
||||
$this->company = $company;
|
||||
$this->csv = $csv;
|
||||
$this->file_name = $file_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
App::setLocale($this->company->getLocale());
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.download_files'))
|
||||
->text('email.admin.download_report_text')
|
||||
->attachData($this->csv, $this->file_name, [
|
||||
'mime' => 'text/csv',
|
||||
])
|
||||
->view('email.admin.download_report', [
|
||||
'logo' => $this->company->present()->logo,
|
||||
'whitelabel' => $this->company->account->isPaid() ? true : false,
|
||||
'settings' => $this->company->settings,
|
||||
'greeting' => $this->company->present()->name(),
|
||||
]);
|
||||
}
|
||||
}
|
@ -47,4 +47,22 @@ class Backup extends BaseModel
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function deleteFile()
|
||||
{
|
||||
|
||||
nlog("deleting => ". $this->filename);
|
||||
|
||||
try{
|
||||
|
||||
Storage::disk(config('filesystems.default'))->delete($this->filename);
|
||||
|
||||
}
|
||||
catch(\Exception $e){
|
||||
|
||||
nlog("BACKUPEXCEPTION deleting backup file with error ". $e->getMessage());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -98,21 +98,6 @@ class CompanyUser extends Pivot
|
||||
public function token()
|
||||
{
|
||||
return $this->hasMany(CompanyToken::class, 'user_id', 'user_id');
|
||||
|
||||
//return $this->hasMany(CompanyToken::class);
|
||||
//return $this->hasOne(CompanyToken::class, 'user_id', 'user_id','company_id', 'company_id');
|
||||
|
||||
|
||||
//return $this->belongsTo(CompanyToken::class, 'user_id', 'user_id');
|
||||
|
||||
// return $this->hasOneThrough(
|
||||
// CompanyToken::class,
|
||||
// CompanyUser::class,
|
||||
// 'user_id', // Foreign key on CompanyUser table...
|
||||
// 'company_id', // Foreign key on CompanyToken table...
|
||||
// 'user_id', // Local key on CompanyToken table...
|
||||
// 'company_id' // Local key on CompanyUser table...
|
||||
// );
|
||||
}
|
||||
|
||||
public function tokens()
|
||||
|
@ -103,7 +103,7 @@ class Gateway extends StaticModel
|
||||
case 20:
|
||||
return [
|
||||
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable','charge.succeeded','payment_intent.succeeded']],
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable','charge.succeeded','payment_intent.succeeded','charge.failed','payment_intent.payment_failed']],
|
||||
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::APPLE_PAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], //Stripe
|
||||
|
@ -179,7 +179,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
||||
$_invoice = collect($this->payment_hash->data->invoices)->first();
|
||||
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
|
||||
|
||||
$this->fee = $this->feeCalc($invoice, $data['total']['amount_with_fee']);
|
||||
// $this->fee = $this->feeCalc($invoice, $data['total']['amount_with_fee']);
|
||||
|
||||
return [
|
||||
'currency' => $this->client->getCurrencyCode(),
|
||||
@ -218,18 +218,6 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
||||
'quantity' => 1,
|
||||
]);
|
||||
|
||||
|
||||
// if($this->fee > 0.1){
|
||||
|
||||
// $items[] = new Item([
|
||||
// 'name' => " ",
|
||||
// 'description' => ctrans('texts.gateway_fee_description'),
|
||||
// 'price' => $this->fee,
|
||||
// 'quantity' => 1,
|
||||
// ]);
|
||||
|
||||
// }
|
||||
|
||||
return $items;
|
||||
|
||||
}
|
||||
|
@ -30,8 +30,12 @@ use App\PaymentDrivers\StripePaymentDriver;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Exception;
|
||||
use Stripe\Customer;
|
||||
use Stripe\Exception\ApiErrorException;
|
||||
use Stripe\Exception\AuthenticationException;
|
||||
use Stripe\Exception\CardException;
|
||||
use Stripe\Exception\InvalidRequestException;
|
||||
use Stripe\Exception\RateLimitException;
|
||||
use Stripe\PaymentIntent;
|
||||
|
||||
class ACH
|
||||
{
|
||||
@ -45,6 +49,9 @@ class ACH
|
||||
$this->stripe = $stripe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize a bank account - requires microdeposit verification
|
||||
*/
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
$data['gateway'] = $this->stripe;
|
||||
@ -135,6 +142,10 @@ class ACH
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a payment WITH instant verification.
|
||||
*/
|
||||
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
$data['gateway'] = $this->stripe;
|
||||
@ -143,6 +154,23 @@ class ACH
|
||||
$data['customer'] = $this->stripe->findOrCreateCustomer();
|
||||
$data['amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency());
|
||||
|
||||
$intent = false;
|
||||
|
||||
if(count($data['tokens']) == 0)
|
||||
{
|
||||
$intent =
|
||||
$this->stripe->createPaymentIntent([
|
||||
'amount' => $data['amount'],
|
||||
'currency' => $data['currency'],
|
||||
'setup_future_usage' => 'off_session',
|
||||
'customer' => $data['customer']->id,
|
||||
'payment_method_types' => ['us_bank_account'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$data['client_secret'] = $intent ? $intent->client_secret : false;
|
||||
|
||||
return render('gateways.stripe.ach.pay', $data);
|
||||
}
|
||||
|
||||
@ -160,6 +188,9 @@ class ACH
|
||||
$description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}";
|
||||
}
|
||||
|
||||
if(substr($cgt->token, 0, 2) === "pm")
|
||||
return $this->paymentIntentTokenBilling($amount, $invoice, $description, $cgt, false);
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
$response = null;
|
||||
@ -203,11 +234,179 @@ class ACH
|
||||
|
||||
}
|
||||
|
||||
public function paymentIntentTokenBilling($amount, $invoice, $description, $cgt, $client_present = true)
|
||||
{
|
||||
$this->stripe->init();
|
||||
|
||||
try {
|
||||
$data = [
|
||||
'amount' => $this->stripe->convertToStripeAmount($amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
|
||||
'currency' => $this->stripe->client->getCurrencyCode(),
|
||||
'payment_method' => $cgt->token,
|
||||
'customer' => $cgt->gateway_customer_reference,
|
||||
'confirm' => true,
|
||||
'description' => $description,
|
||||
'metadata' => [
|
||||
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||
'gateway_type_id' => $cgt->gateway_type_id,
|
||||
],
|
||||
];
|
||||
|
||||
if($cgt->gateway_type_id == GatewayType::BANK_TRANSFER)
|
||||
$data['payment_method_types'] = ['us_bank_account'];
|
||||
|
||||
$response = $this->stripe->createPaymentIntent($data, $this->stripe->stripe_connect_auth);
|
||||
|
||||
SystemLogger::dispatch($response, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client, $this->stripe->client->company);
|
||||
|
||||
}catch(\Exception $e) {
|
||||
|
||||
$data =[
|
||||
'status' => '',
|
||||
'error_type' => '',
|
||||
'error_code' => '',
|
||||
'param' => '',
|
||||
'message' => '',
|
||||
];
|
||||
|
||||
switch ($e) {
|
||||
case ($e instanceof CardException):
|
||||
$data['status'] = $e->getHttpStatus();
|
||||
$data['error_type'] = $e->getError()->type;
|
||||
$data['error_code'] = $e->getError()->code;
|
||||
$data['param'] = $e->getError()->param;
|
||||
$data['message'] = $e->getError()->message;
|
||||
break;
|
||||
case ($e instanceof RateLimitException):
|
||||
$data['message'] = 'Too many requests made to the API too quickly';
|
||||
break;
|
||||
case ($e instanceof InvalidRequestException):
|
||||
$data['message'] = 'Invalid parameters were supplied to Stripe\'s API';
|
||||
break;
|
||||
case ($e instanceof AuthenticationException):
|
||||
$data['message'] = 'Authentication with Stripe\'s API failed';
|
||||
break;
|
||||
case ($e instanceof ApiErrorException):
|
||||
$data['message'] = 'Network communication with Stripe failed';
|
||||
break;
|
||||
|
||||
default:
|
||||
$data['message'] = $e->getMessage();
|
||||
break;
|
||||
}
|
||||
|
||||
$this->stripe->processInternallyFailedPayment($this->stripe, $e);
|
||||
|
||||
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client, $this->stripe->client->company);
|
||||
}
|
||||
|
||||
if (! $response) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$payment_method_type = PaymentType::ACH;
|
||||
|
||||
$data = [
|
||||
'gateway_type_id' => $cgt->gateway_type_id,
|
||||
'payment_type' => PaymentType::ACH,
|
||||
'transaction_reference' => $response->charges->data[0]->id,
|
||||
'amount' => $amount,
|
||||
];
|
||||
|
||||
$payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING);
|
||||
$payment->meta = $cgt->meta;
|
||||
$payment->save();
|
||||
|
||||
$this->stripe->payment_hash->payment_id = $payment->id;
|
||||
$this->stripe->payment_hash->save();
|
||||
|
||||
if($client_present){
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
return $payment;
|
||||
}
|
||||
|
||||
public function handlePaymentIntentResponse($request)
|
||||
{
|
||||
|
||||
$response = json_decode($request->gateway_response);
|
||||
$bank_account_response = json_decode($request->bank_account_response);
|
||||
|
||||
$method = $bank_account_response->payment_method->us_bank_account;
|
||||
$method->id = $response->payment_method;
|
||||
$method->state = 'authorized';
|
||||
|
||||
$this->stripe->payment_hash = PaymentHash::where("hash", $request->input("payment_hash"))->first();
|
||||
|
||||
if($response->id && $response->status === "processing") {
|
||||
$payment_intent = PaymentIntent::retrieve($response->id, $this->stripe->stripe_connect_auth);
|
||||
|
||||
$state = [
|
||||
'gateway_type_id' => GatewayType::BANK_TRANSFER,
|
||||
'amount' => $response->amount,
|
||||
'currency' => $response->currency,
|
||||
'customer' => $request->customer,
|
||||
'source' => $response->payment_method,
|
||||
'charge' => $response
|
||||
];
|
||||
|
||||
$this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state);
|
||||
$this->stripe->payment_hash->save();
|
||||
|
||||
$customer = $this->stripe->getCustomer($request->customer);
|
||||
|
||||
$this->storePaymentMethod($method, GatewayType::BANK_TRANSFER, $customer);
|
||||
|
||||
return $this->processPendingPayment($state, true);
|
||||
}
|
||||
|
||||
if($response->next_action){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function processPendingPaymentIntent($state, $client_present = true)
|
||||
{
|
||||
$this->stripe->init();
|
||||
|
||||
$data = [
|
||||
'payment_method' => $state['source'],
|
||||
'payment_type' => PaymentType::ACH,
|
||||
'amount' => $state['amount'],
|
||||
'transaction_reference' => $state['charge'],
|
||||
'gateway_type_id' => GatewayType::BANK_TRANSFER,
|
||||
];
|
||||
|
||||
$payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $state, 'data' => $data],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_STRIPE,
|
||||
$this->stripe->client,
|
||||
$this->stripe->client->company,
|
||||
);
|
||||
|
||||
if(!$client_present)
|
||||
return $payment;
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function paymentResponse($request)
|
||||
{
|
||||
|
||||
$this->stripe->init();
|
||||
|
||||
//it may be a payment intent here.
|
||||
if($request->input('client_secret') != '')
|
||||
return $this->handlePaymentIntentResponse($request);
|
||||
|
||||
$source = ClientGatewayToken::query()
|
||||
->where('id', $this->decodePrimaryKey($request->source))
|
||||
->where('company_id', auth()->guard('contact')->user()->client->company->id)
|
||||
@ -242,6 +441,9 @@ class ACH
|
||||
$description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}";
|
||||
}
|
||||
|
||||
if(substr($source->token, 0, 2) === "pm")
|
||||
return $this->paymentIntentTokenBilling($amount, $invoice, $description, $source);
|
||||
|
||||
try {
|
||||
$state['charge'] = \Stripe\Charge::create([
|
||||
'amount' => $state['amount'],
|
||||
@ -270,6 +472,7 @@ class ACH
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function processPendingPayment($state, $client_present = true)
|
||||
{
|
||||
$this->stripe->init();
|
||||
@ -321,12 +524,14 @@ class ACH
|
||||
|
||||
private function storePaymentMethod($method, $payment_method_id, $customer)
|
||||
{
|
||||
$state = property_exists($method, 'state') ? $method->state : 'unauthorized';
|
||||
|
||||
try {
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->brand = (string) \sprintf('%s (%s)', $method->bank_name, ctrans('texts.ach'));
|
||||
$payment_meta->last4 = (string) $method->last4;
|
||||
$payment_meta->type = GatewayType::BANK_TRANSFER;
|
||||
$payment_meta->state = 'unauthorized';
|
||||
$payment_meta->state = $state;
|
||||
|
||||
$data = [
|
||||
'payment_meta' => $payment_meta,
|
||||
|
128
app/PaymentDrivers/Stripe/Jobs/PaymentIntentFailureWebhook.php
Normal file
128
app/PaymentDrivers/Stripe/Jobs/PaymentIntentFailureWebhook.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Stripe\Jobs;
|
||||
|
||||
use App\Jobs\Mail\PaymentFailedMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\Stripe\Utilities;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class PaymentIntentFailureWebhook implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Utilities;
|
||||
|
||||
public $tries = 1; //number of retries
|
||||
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
public $stripe_request;
|
||||
|
||||
public $company_key;
|
||||
|
||||
private $company_gateway_id;
|
||||
|
||||
public $payment_completed = false;
|
||||
|
||||
public function __construct($stripe_request, $company_key, $company_gateway_id)
|
||||
{
|
||||
$this->stripe_request = $stripe_request;
|
||||
$this->company_key = $company_key;
|
||||
$this->company_gateway_id = $company_gateway_id;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
||||
|
||||
$company = Company::where('company_key', $this->company_key)->first();
|
||||
|
||||
foreach ($this->stripe_request as $transaction) {
|
||||
|
||||
if(array_key_exists('payment_intent', $transaction))
|
||||
{
|
||||
|
||||
$payment = Payment::query()
|
||||
->where('company_id', $company->id)
|
||||
->where(function ($query) use ($transaction) {
|
||||
$query->where('transaction_reference', $transaction['payment_intent'])
|
||||
->orWhere('transaction_reference', $transaction['id']);
|
||||
})
|
||||
->first();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
$payment = Payment::query()
|
||||
->where('company_id', $company->id)
|
||||
->where('transaction_reference', $transaction['id'])
|
||||
->first();
|
||||
|
||||
}
|
||||
|
||||
if ($payment) {
|
||||
|
||||
$client = $payment->client;
|
||||
|
||||
if($payment->status_id == Payment::STATUS_PENDING)
|
||||
$payment->service()->deletePayment();
|
||||
|
||||
$payment->status_id = Payment::STATUS_FAILED;
|
||||
$payment->save();
|
||||
|
||||
$payment_hash = PaymentHash::where('payment_id', $payment->id)->first();
|
||||
|
||||
if($payment_hash)
|
||||
{
|
||||
|
||||
$error = ctrans('texts.client_payment_failure_body', [
|
||||
'invoice' => implode(",", $payment->invoices->pluck('number')->toArray()),
|
||||
'amount' => array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total]);
|
||||
|
||||
}
|
||||
else
|
||||
$error = "Payment for " . $payment->client->present()->name(). " for {$payment->amount} failed";
|
||||
|
||||
if(array_key_exists('failure_message', $transaction)){
|
||||
|
||||
$error .= "\n\n" .$transaction['failure_message'];
|
||||
}
|
||||
|
||||
PaymentFailedMailer::dispatch(
|
||||
$payment_hash,
|
||||
$client->company,
|
||||
$client,
|
||||
$error
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -53,10 +53,6 @@ class PaymentIntentWebhook implements ShouldQueue
|
||||
|
||||
public function handle()
|
||||
{
|
||||
// nlog($this->stripe_request);
|
||||
// nlog(optional($this->stripe_request['object']['charges']['data'][0]['metadata'])['gateway_type_id']);
|
||||
// nlog(optional($this->stripe_request['object']['charges']['data'][0]['metadata'])['payment_hash']);
|
||||
// nlog(optional($this->stripe_request['object']['charges']['data'][0]['payment_method_details']['card'])['brand']);
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
||||
|
||||
|
@ -38,6 +38,7 @@ use App\PaymentDrivers\Stripe\EPS;
|
||||
use App\PaymentDrivers\Stripe\FPX;
|
||||
use App\PaymentDrivers\Stripe\GIROPAY;
|
||||
use App\PaymentDrivers\Stripe\ImportCustomers;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook;
|
||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentWebhook;
|
||||
use App\PaymentDrivers\Stripe\PRZELEWY24;
|
||||
use App\PaymentDrivers\Stripe\SEPA;
|
||||
@ -47,6 +48,7 @@ use App\PaymentDrivers\Stripe\Utilities;
|
||||
use App\PaymentDrivers\Stripe\iDeal;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Exception;
|
||||
use Google\Service\ServiceConsumerManagement\CustomError;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Laracasts\Presenter\Exceptions\PresenterException;
|
||||
@ -409,6 +411,16 @@ class StripePaymentDriver extends BaseDriver
|
||||
return $this->company_gateway->getPublishableKey();
|
||||
}
|
||||
|
||||
public function getCustomer($customer_id) :?Customer
|
||||
{
|
||||
$customer = Customer::retrieve($customer_id, $this->stripe_connect_auth);
|
||||
|
||||
if($customer)
|
||||
return $customer;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds or creates a Stripe Customer object.
|
||||
*
|
||||
@ -568,15 +580,21 @@ class StripePaymentDriver extends BaseDriver
|
||||
|
||||
//payment_intent.succeeded - this will confirm or cancel the payment
|
||||
if($request->type === 'payment_intent.succeeded'){
|
||||
PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(10));
|
||||
PaymentIntentWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(2,10)));
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
if(in_array($request->type, ['payment_intent.payment_failed','charge.failed'])){
|
||||
PaymentIntentFailureWebhook::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(2,10)));
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
|
||||
if ($request->type === 'charge.succeeded') {
|
||||
|
||||
foreach ($request->data as $transaction) {
|
||||
|
||||
if(array_key_exists('payment_intent', $transaction))
|
||||
if(array_key_exists('payment_intent', $transaction) && $transaction['payment_intent'])
|
||||
{
|
||||
$payment = Payment::query()
|
||||
// ->where('company_id', $request->getCompany()->id)
|
||||
|
@ -117,7 +117,7 @@ trait ChartQueries
|
||||
GROUP BY invoices.date
|
||||
HAVING currency_id = :currency_id
|
||||
"), [
|
||||
'company_currency' => $this->company->settings->currency_id,
|
||||
'company_currency' => (int)$this->company->settings->currency_id,
|
||||
'currency_id' => $currency_id,
|
||||
'company_id' => $this->company->id,
|
||||
'start_date' => $start_date,
|
||||
|
@ -123,13 +123,14 @@ class AutoBillInvoice extends AbstractService
|
||||
->setPaymentHash($payment_hash)
|
||||
->tokenBilling($gateway_token, $payment_hash);
|
||||
} catch (\Exception $e) {
|
||||
$this->invoice->auto_bill_tries = $number_of_retries + 1;
|
||||
$this->invoice->auto_bill_tries += 1;
|
||||
|
||||
if ($this->invoice->auto_bill_tries == 3) {
|
||||
$this->invoice->auto_bill_enabled = false;
|
||||
$this->invoice->auto_bill_tries = 0; //reset the counter here in case auto billing is turned on again in the future.
|
||||
$this->invoice->save();
|
||||
}
|
||||
|
||||
nlog("payment NOT captured for " . $this->invoice->number . " with error " . $e->getMessage());
|
||||
}
|
||||
|
||||
|
@ -46,14 +46,6 @@ class DeletePayment
|
||||
->save();
|
||||
}
|
||||
|
||||
//reverse paymentables->invoices
|
||||
|
||||
//reverse paymentables->credits
|
||||
|
||||
//set refunded to amount
|
||||
|
||||
//set applied amount to 0
|
||||
|
||||
private function cleanupPayment()
|
||||
{
|
||||
$this->payment->is_deleted = true;
|
||||
|
@ -66,7 +66,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
'timezone' => env('SERVER_TIMEZONE', 'UTC'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -54,7 +54,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'asset_url' => env('ASSET_URL', null),
|
||||
'asset_url' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.3.89',
|
||||
'app_tag' => '5.3.89',
|
||||
'app_version' => '5.3.90',
|
||||
'app_tag' => '5.3.90',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
@ -155,6 +155,10 @@ return [
|
||||
'designs' => [
|
||||
'base_path' => resource_path('views/pdf-designs/'),
|
||||
],
|
||||
'maintenance' => [
|
||||
'delete_pdfs' => env('DELETE_PDF_DAYS', 0),
|
||||
'delete_backups' => env('DELETE_BACKUP_DAYS', 0),
|
||||
],
|
||||
'log_pdf_html' => env('LOG_PDF_HTML', false),
|
||||
'expanded_logging' => env('EXPANDED_LOGGING', false),
|
||||
'snappdf_chromium_path' => env('SNAPPDF_CHROMIUM_PATH', false),
|
||||
|
@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateCustomValueFourColumns extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
if(Ninja::isSelfHost())
|
||||
{
|
||||
|
||||
Schema::table('credits', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('client_contacts', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('clients', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('clients', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('documents', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('expenses', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('invoices', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('payments', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('projects', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('quotes', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('recurring_invoices', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('recurring_quotes', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('recurring_expenses', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('tasks', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('vendors', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
Schema::table('vendor_contacts', function (Blueprint $table) {
|
||||
|
||||
$table->text('custom_value4')->change();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
62
public/flutter_service_worker.js
vendored
62
public/flutter_service_worker.js
vendored
@ -3,43 +3,43 @@ const MANIFEST = 'flutter-app-manifest';
|
||||
const TEMP = 'flutter-temp-cache';
|
||||
const CACHE_NAME = 'flutter-app-cache';
|
||||
const RESOURCES = {
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"main.dart.js": "0b89ccdb131cf8a2d7bdb28e5373c68f",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487",
|
||||
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900",
|
||||
"canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd",
|
||||
"main.dart.js": "364188203aa14bd63e2bc6012e03dca0",
|
||||
"canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba",
|
||||
"canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd",
|
||||
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900",
|
||||
"canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487",
|
||||
"flutter.js": "0816e65a103ba8ba51b174eeeeb2cb67",
|
||||
"/": "981a36b4c01bd0159f59c3f0b234ce5c",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"version.json": "3afb81924daf4f751571755436069115",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "b62641afc9ab487008e996a5c5865e56",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b",
|
||||
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
|
||||
"assets/NOTICES": "52d7174bb068ef86545951d5bc8c5744",
|
||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
|
||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
|
||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
|
||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
|
||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "b62641afc9ab487008e996a5c5865e56",
|
||||
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
|
||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
|
||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
|
||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
|
||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
|
||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
||||
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
|
||||
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
|
||||
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
|
||||
"flutter.js": "0816e65a103ba8ba51b174eeeeb2cb67",
|
||||
"/": "d58545047c6b4aa7c8c528dc89852fb6",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40"
|
||||
"assets/NOTICES": "52d7174bb068ef86545951d5bc8c5744",
|
||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628"
|
||||
};
|
||||
|
||||
// The application shell files that are downloaded before a service worker can
|
||||
|
1
public/images/checkmark-round.svg
Normal file
1
public/images/checkmark-round.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="47" height="47" fill="none" xmlns:v="https://vecta.io/nano"><g filter="url(#A)"><circle cx="23.5" cy="19.5" r="12.5" fill="#fff"/></g><path d="M28.949 15.974l-7.051 7.051-3.205-3.205" stroke="#0091ea" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><defs><filter id="A" x="0" y="0" width="47" height="47" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="A"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4"/><feGaussianBlur stdDeviation="5.5"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.16 0"/><feBlend in2="A"/><feBlend in="SourceGraphic"/></filter></defs></svg>
|
After Width: | Height: | Size: 747 B |
1
public/images/checkmark.svg
Normal file
1
public/images/checkmark.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" xmlns:v="https://vecta.io/nano"><path d="M12.821 3.846l-7.051 7.051-3.205-3.205" stroke="#0091ea" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
After Width: | Height: | Size: 244 B |
11
public/images/test.svg
Normal file
11
public/images/test.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 598 450" width="598" height="450">
|
||||
<title>Vector 1580-svg</title>
|
||||
<defs>
|
||||
<image width="595" height="450" id="img1" href="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTk1IiBoZWlnaHQ9IjQ1MCIgdmlld0JveD0iMCAwIDU5NSA0NTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIG9wYWNpdHk9IjAuMiIgZD0iTTQ3NC45NDIgMTUzLjQ4N0M1MzguMDc2IDEzMS4wMzQgNjAzLjE3IDEwLjk1MTMgNTkyLjgyMyAtNDFMLTcyLjE5NzYgLTIuMzY2NzRMLTkzIDU0NS40MzNDLTUxLjUwNSA1MzYuMDc3IDE5LjczNDMgNTMxLjU2NSA3MS4xMDg3IDQ2OS40ODdDMTU1LjgyMiAzNjcuMTI2IDIzOS4xOCA0MzEuODQ1IDI4NS4wNzggMzg2LjkzOEMzMzAuOTc1IDM0Mi4wMzEgMzE1LjEyNiAyOTkuNzY1IDMzMC42NDUgMjQ0Ljk1MkMzNDYuMTY0IDE5MC4xMzkgMzk2LjAyNSAxODEuNTU0IDQ3NC45NDIgMTUzLjQ4N1oiIHN0cm9rZT0id2hpdGUiLz4KPC9zdmc+Cg=="/>
|
||||
</defs>
|
||||
<style>
|
||||
.s0 { opacity: .1;fill: #ffffff;stroke: #ffffff;stroke-width: 7 }
|
||||
</style>
|
||||
<path id="Layer" class="s0" d="m330.5 241c-15.5 54.9 0.4 97.1-45.5 142-45.9 44.9-129.3-19.8-214 82.6-51.4 62.1-122.6 66.6-164.1 75.9l20.8-547.8 665-38.6c10.4 51.9-54.7 172-117.9 194.5-78.9 28-128.8 36.6-144.3 91.4z"/>
|
||||
<use id="Vector 1581" href="#img1" transform="matrix(1,0,0,1,0,0)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
2
public/js/clients/payments/stripe-ach.js
vendored
2
public/js/clients/payments/stripe-ach.js
vendored
@ -1,2 +1,2 @@
|
||||
/*! For license information please see stripe-ach.js.LICENSE.txt */
|
||||
(()=>{function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function t(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}(new(function(){function n(){var e,r=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),t(this,"setupStripe",(function(){return r.stripeConnect?r.stripe=Stripe(r.key,{stripeAccount:r.stripeConnect}):r.stripe=Stripe(r.key),r})),t(this,"getFormData",(function(){return{country:document.getElementById("country").value,currency:document.getElementById("currency").value,routing_number:document.getElementById("routing-number").value,account_number:document.getElementById("account-number").value,account_holder_name:document.getElementById("account-holder-name").value,account_holder_type:document.querySelector('input[name="account-holder-type"]:checked').value}})),t(this,"handleError",(function(e){document.getElementById("save-button").disabled=!1,document.querySelector("#save-button > svg").classList.add("hidden"),document.querySelector("#save-button > span").classList.remove("hidden"),r.errors.textContent="",r.errors.textContent=e,r.errors.hidden=!1})),t(this,"handleSuccess",(function(e){document.getElementById("gateway_response").value=JSON.stringify(e),document.getElementById("server_response").submit()})),t(this,"handleSubmit",(function(e){document.getElementById("save-button").disabled=!0,document.querySelector("#save-button > svg").classList.remove("hidden"),document.querySelector("#save-button > span").classList.add("hidden"),e.preventDefault(),r.errors.textContent="",r.errors.hidden=!0,r.stripe.createToken("bank_account",r.getFormData()).then((function(e){return e.hasOwnProperty("error")?r.handleError(e.error.message):r.handleSuccess(e)}))})),this.errors=document.getElementById("errors"),this.key=document.querySelector('meta[name="stripe-publishable-key"]').content,this.stripe_connect=null===(e=document.querySelector('meta[name="stripe-account-id"]'))||void 0===e?void 0:e.content}var r,o,u;return r=n,(o=[{key:"handle",value:function(){var e=this;document.getElementById("save-button").addEventListener("click",(function(t){return e.handleSubmit(t)}))}}])&&e(r.prototype,o),u&&e(r,u),n}())).setupStripe().handle()})();
|
||||
(()=>{function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function t(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}(new(function(){function n(){var e,r=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),t(this,"setupStripe",(function(){return r.stripeConnect?r.stripe=Stripe(r.key,{stripeAccount:r.stripeConnect}):r.stripe=Stripe(r.key),r})),t(this,"getFormData",(function(){return{country:document.getElementById("country").value,currency:document.getElementById("currency").value,routing_number:document.getElementById("routing-number").value,account_number:document.getElementById("account-number").value,account_holder_name:document.getElementById("account-holder-name").value,account_holder_type:document.querySelector('input[name="account-holder-type"]:checked').value}})),t(this,"handleError",(function(e){document.getElementById("save-button").disabled=!1,document.querySelector("#save-button > svg").classList.add("hidden"),document.querySelector("#save-button > span").classList.remove("hidden"),r.errors.textContent="",r.errors.textContent=e,r.errors.hidden=!1})),t(this,"handleSuccess",(function(e){document.getElementById("gateway_response").value=JSON.stringify(e),document.getElementById("server_response").submit()})),t(this,"handleSubmit",(function(e){if(!document.getElementById("accept-terms").checked)return errors.textContent="You must accept the mandate terms prior to making payment.",void(errors.hidden=!1);document.getElementById("save-button").disabled=!0,document.querySelector("#save-button > svg").classList.remove("hidden"),document.querySelector("#save-button > span").classList.add("hidden"),e.preventDefault(),r.errors.textContent="",r.errors.hidden=!0,r.stripe.createToken("bank_account",r.getFormData()).then((function(e){return e.hasOwnProperty("error")?r.handleError(e.error.message):r.handleSuccess(e)}))})),this.errors=document.getElementById("errors"),this.key=document.querySelector('meta[name="stripe-publishable-key"]').content,this.stripe_connect=null===(e=document.querySelector('meta[name="stripe-account-id"]'))||void 0===e?void 0:e.content}var r,o,u;return r=n,(o=[{key:"handle",value:function(){var e=this;document.getElementById("save-button").addEventListener("click",(function(t){return e.handleSubmit(t)}))}}])&&e(r.prototype,o),u&&e(r,u),n}())).setupStripe().handle()})();
|
15
public/main.dart.js
vendored
15
public/main.dart.js
vendored
@ -181524,8 +181524,9 @@ n=o?n:n-b.id
|
||||
m=o?b.b:b.garI()
|
||||
if(h.x==="-1"){h=s.bf.f
|
||||
h=r!==(h==null?"1":h)}else h=!1
|
||||
if(h){l=b.x1
|
||||
if(!(l!==1&&l!==0)){h=s.bf.f
|
||||
if(h){h=b.x1
|
||||
if(h!==1&&h!==0)l=1/h
|
||||
else{h=s.bf.f
|
||||
if(h==null)h="1"
|
||||
l=A.zV(k.r,r,h)}n*=l
|
||||
m*=l}h=p.h(0,j)
|
||||
@ -181566,8 +181567,9 @@ m=b.a
|
||||
m=n?m:m-b.id
|
||||
if(r.x==="-1"){r=q.bf.f
|
||||
r=p!==(r==null?"1":r)}else r=!1
|
||||
if(r){l=b.x1
|
||||
if(!(l!==1&&l!==0)){r=q.bf.f
|
||||
if(r){r=b.x1
|
||||
if(r!==1&&r!==0)l=1/r
|
||||
else{r=q.bf.f
|
||||
if(r==null)r="1"
|
||||
l=A.zV(k.w,p,r)}m*=l}r=o.h(0,j)
|
||||
r.v(0,s,r.h(0,s)+m)
|
||||
@ -181675,8 +181677,9 @@ p=A.abG(r,s,l.x,l.f,q)*A.cF(B.e.cp(b.a,1e6)/3600,3)
|
||||
if(l.y.x==="-1"){o=r.k2.f
|
||||
n=s.bf.f
|
||||
o=o!==(n==null?"1":n)}else o=!1
|
||||
if(o){m=l.z.x1
|
||||
if(!(m!==1&&m!==0)){r=r.k2.f
|
||||
if(o){o=l.z.x1
|
||||
if(o!==1&&o!==0)m=1/o
|
||||
else{r=r.k2.f
|
||||
s=s.bf.f
|
||||
if(s==null)s="1"
|
||||
m=A.zV(l.Q,r,s)}p*=m}s=q.d
|
||||
|
File diff suppressed because one or more lines are too long
15
public/main.foss.dart.js
vendored
15
public/main.foss.dart.js
vendored
@ -181295,8 +181295,9 @@ n=o?n:n-b.id
|
||||
m=o?b.b:b.garC()
|
||||
if(h.x==="-1"){h=s.bf.f
|
||||
h=r!==(h==null?"1":h)}else h=!1
|
||||
if(h){l=b.x1
|
||||
if(!(l!==1&&l!==0)){h=s.bf.f
|
||||
if(h){h=b.x1
|
||||
if(h!==1&&h!==0)l=1/h
|
||||
else{h=s.bf.f
|
||||
if(h==null)h="1"
|
||||
l=A.zT(k.r,r,h)}n*=l
|
||||
m*=l}h=p.h(0,j)
|
||||
@ -181337,8 +181338,9 @@ m=b.a
|
||||
m=n?m:m-b.id
|
||||
if(r.x==="-1"){r=q.bf.f
|
||||
r=p!==(r==null?"1":r)}else r=!1
|
||||
if(r){l=b.x1
|
||||
if(!(l!==1&&l!==0)){r=q.bf.f
|
||||
if(r){r=b.x1
|
||||
if(r!==1&&r!==0)l=1/r
|
||||
else{r=q.bf.f
|
||||
if(r==null)r="1"
|
||||
l=A.zT(k.w,p,r)}m*=l}r=o.h(0,j)
|
||||
r.v(0,s,r.h(0,s)+m)
|
||||
@ -181446,8 +181448,9 @@ p=A.abH(r,s,l.x,l.f,q)*A.cF(B.e.cp(b.a,1e6)/3600,3)
|
||||
if(l.y.x==="-1"){o=r.k2.f
|
||||
n=s.bf.f
|
||||
o=o!==(n==null?"1":n)}else o=!1
|
||||
if(o){m=l.z.x1
|
||||
if(!(m!==1&&m!==0)){r=r.k2.f
|
||||
if(o){o=l.z.x1
|
||||
if(o!==1&&o!==0)m=1/o
|
||||
else{r=r.k2.f
|
||||
s=s.bf.f
|
||||
if(s==null)s="1"
|
||||
m=A.zT(l.Q,r,s)}p*=m}s=q.d
|
||||
|
File diff suppressed because one or more lines are too long
15
public/main.html.dart.js
vendored
15
public/main.html.dart.js
vendored
@ -177616,8 +177616,9 @@ n=o?n:n-b.id
|
||||
m=o?b.b:b.gaq4()
|
||||
if(h.x==="-1"){h=s.be.f
|
||||
h=r!==(h==null?"1":h)}else h=!1
|
||||
if(h){l=b.x1
|
||||
if(!(l!==1&&l!==0)){h=s.be.f
|
||||
if(h){h=b.x1
|
||||
if(h!==1&&h!==0)l=1/h
|
||||
else{h=s.be.f
|
||||
if(h==null)h="1"
|
||||
l=A.zq(k.r,r,h)}n*=l
|
||||
m*=l}h=p.h(0,j)
|
||||
@ -177658,8 +177659,9 @@ m=b.a
|
||||
m=n?m:m-b.id
|
||||
if(r.x==="-1"){r=q.be.f
|
||||
r=p!==(r==null?"1":r)}else r=!1
|
||||
if(r){l=b.x1
|
||||
if(!(l!==1&&l!==0)){r=q.be.f
|
||||
if(r){r=b.x1
|
||||
if(r!==1&&r!==0)l=1/r
|
||||
else{r=q.be.f
|
||||
if(r==null)r="1"
|
||||
l=A.zq(k.w,p,r)}m*=l}r=o.h(0,j)
|
||||
r.v(0,s,r.h(0,s)+m)
|
||||
@ -177767,8 +177769,9 @@ p=A.a9Y(r,s,l.x,l.f,q)*A.cD(B.e.co(b.a,1e6)/3600,3)
|
||||
if(l.y.x==="-1"){o=r.k2.f
|
||||
n=s.be.f
|
||||
o=o!==(n==null?"1":n)}else o=!1
|
||||
if(o){m=l.z.x1
|
||||
if(!(m!==1&&m!==0)){r=r.k2.f
|
||||
if(o){o=l.z.x1
|
||||
if(o!==1&&o!==0)m=1/o
|
||||
else{r=r.k2.f
|
||||
s=s.be.f
|
||||
if(s==null)s="1"
|
||||
m=A.zq(l.Q,r,s)}p*=m}s=q.d
|
||||
|
15
public/main.next.dart.js
vendored
15
public/main.next.dart.js
vendored
@ -181783,8 +181783,9 @@ n=o?n:n-b.id
|
||||
m=o?b.b:b.garR()
|
||||
if(h.x==="-1"){h=s.bf.f
|
||||
h=r!==(h==null?"1":h)}else h=!1
|
||||
if(h){l=b.x1
|
||||
if(!(l!==1&&l!==0)){h=s.bf.f
|
||||
if(h){h=b.x1
|
||||
if(h!==1&&h!==0)l=1/h
|
||||
else{h=s.bf.f
|
||||
if(h==null)h="1"
|
||||
l=A.zY(k.r,r,h)}n*=l
|
||||
m*=l}h=p.h(0,j)
|
||||
@ -181825,8 +181826,9 @@ m=b.a
|
||||
m=n?m:m-b.id
|
||||
if(r.x==="-1"){r=q.bf.f
|
||||
r=p!==(r==null?"1":r)}else r=!1
|
||||
if(r){l=b.x1
|
||||
if(!(l!==1&&l!==0)){r=q.bf.f
|
||||
if(r){r=b.x1
|
||||
if(r!==1&&r!==0)l=1/r
|
||||
else{r=q.bf.f
|
||||
if(r==null)r="1"
|
||||
l=A.zY(k.w,p,r)}m*=l}r=o.h(0,j)
|
||||
r.v(0,s,r.h(0,s)+m)
|
||||
@ -181934,8 +181936,9 @@ p=A.abQ(r,s,l.x,l.f,q)*A.cF(B.e.cq(b.a,1e6)/3600,3)
|
||||
if(l.y.x==="-1"){o=r.k2.f
|
||||
n=s.bf.f
|
||||
o=o!==(n==null?"1":n)}else o=!1
|
||||
if(o){m=l.z.x1
|
||||
if(!(m!==1&&m!==0)){r=r.k2.f
|
||||
if(o){o=l.z.x1
|
||||
if(o!==1&&o!==0)m=1/o
|
||||
else{r=r.k2.f
|
||||
s=s.bf.f
|
||||
if(s==null)s="1"
|
||||
m=A.zY(l.Q,r,s)}p*=m}s=q.d
|
||||
|
18
public/main.profile.dart.js
vendored
18
public/main.profile.dart.js
vendored
@ -347102,8 +347102,10 @@
|
||||
} else
|
||||
t1 = false;
|
||||
if (t1) {
|
||||
exchangeRate = invoice.exchangeRate;
|
||||
if (!(exchangeRate !== 1 && exchangeRate !== 0)) {
|
||||
t1 = invoice.exchangeRate;
|
||||
if (t1 !== 1 && t1 !== 0)
|
||||
exchangeRate = 1 / t1;
|
||||
else {
|
||||
t1 = t2.settings.currencyId;
|
||||
if (t1 == null)
|
||||
t1 = "1";
|
||||
@ -347179,8 +347181,10 @@
|
||||
} else
|
||||
t2 = false;
|
||||
if (t2) {
|
||||
exchangeRate = quote.exchangeRate;
|
||||
if (!(exchangeRate !== 1 && exchangeRate !== 0)) {
|
||||
t2 = quote.exchangeRate;
|
||||
if (t2 !== 1 && t2 !== 0)
|
||||
exchangeRate = 1 / t2;
|
||||
else {
|
||||
t2 = t3.settings.currencyId;
|
||||
if (t2 == null)
|
||||
t2 = "1";
|
||||
@ -347380,8 +347384,10 @@
|
||||
} else
|
||||
t5 = false;
|
||||
if (t5) {
|
||||
exchangeRate = _this.invoice.exchangeRate;
|
||||
if (!(exchangeRate !== 1 && exchangeRate !== 0)) {
|
||||
t5 = _this.invoice.exchangeRate;
|
||||
if (t5 !== 1 && t5 !== 0)
|
||||
exchangeRate = 1 / t5;
|
||||
else {
|
||||
t3 = t3.settings.currencyId;
|
||||
t2 = t2.settings.currencyId;
|
||||
if (t2 == null)
|
||||
|
@ -2,7 +2,7 @@
|
||||
"/js/app.js": "/js/app.js?id=0e3959ab851d3350364d",
|
||||
"/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=de4468c682d6861847de",
|
||||
"/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=cfe5de1cf87a0b01568d",
|
||||
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=a5f14c885c3aeef6c744",
|
||||
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=50d964c4a3ffa7f2f99f",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=6b79265cbb8c963eef19",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=2cccf9e51b60a0ab17b8",
|
||||
"/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=22fc06e698dea2c3bdf3",
|
||||
|
2
public/vendor/livewire/livewire.js
vendored
2
public/vendor/livewire/livewire.js
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/livewire/livewire.js.map
vendored
2
public/vendor/livewire/livewire.js.map
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/livewire/manifest.json
vendored
2
public/vendor/livewire/manifest.json
vendored
@ -1 +1 @@
|
||||
{"/livewire.js":"/livewire.js?id=940557fc56b15ccb9a2d"}
|
||||
{"/livewire.js":"/livewire.js?id=c69d0f2801c01fcf8166"}
|
7
resources/js/clients/payments/stripe-ach.js
vendored
7
resources/js/clients/payments/stripe-ach.js
vendored
@ -70,6 +70,13 @@ class AuthorizeACH {
|
||||
};
|
||||
|
||||
handleSubmit = (e) => {
|
||||
|
||||
if (!document.getElementById('accept-terms').checked) {
|
||||
errors.textContent = "You must accept the mandate terms prior to making payment.";
|
||||
errors.hidden = false;
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('save-button').disabled = true;
|
||||
document.querySelector('#save-button > svg').classList.remove('hidden');
|
||||
document.querySelector('#save-button > span').classList.add('hidden');
|
||||
|
@ -4583,6 +4583,8 @@ $LANG = array(
|
||||
'alternate_pdf_viewer' => 'Alternate PDF Viewer',
|
||||
'alternate_pdf_viewer_help' => 'Improve scrolling over the PDF preview [BETA]',
|
||||
'currency_cayman_island_dollar' => 'Cayman Island Dollar',
|
||||
'download_report_description' => 'Please see attached file to check your report.'
|
||||
|
||||
|
||||
);
|
||||
|
||||
|
7
resources/views/email/admin/download_report.blade.php
Normal file
7
resources/views/email/admin/download_report.blade.php
Normal file
@ -0,0 +1,7 @@
|
||||
@component('email.template.admin', ['logo' => $logo, 'settings' => $settings])
|
||||
<div class="center">
|
||||
<h1>{{ ctrans('texts.download_files') }}</h1>
|
||||
<p>{{ ctrans('texts.download_report_description') }}</p>
|
||||
|
||||
</div>
|
||||
@endcomponent
|
@ -0,0 +1,4 @@
|
||||
{!! ctrans('texts.download_files') !!}
|
||||
|
||||
{!! ctrans('texts.download_report_description') !!}
|
||||
|
@ -105,7 +105,7 @@
|
||||
|
||||
[data-ref="table"] {
|
||||
margin-top: 0.5rem;
|
||||
/* margin-bottom: 200px; */
|
||||
margin-bottom: 50px;
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
|
@ -101,7 +101,7 @@
|
||||
|
||||
[data-ref="table"] {
|
||||
margin-top: 1rem;
|
||||
/* margin-bottom: 200px; */
|
||||
margin-bottom: 50px;
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
|
@ -95,7 +95,7 @@
|
||||
}
|
||||
|
||||
[data-ref="table"] {
|
||||
/* margin-bottom: 200px; */
|
||||
margin-bottom: 50px;
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
|
@ -95,7 +95,7 @@
|
||||
|
||||
[data-ref="table"] {
|
||||
margin-top: 3rem;
|
||||
/* margin-bottom: 200px; */
|
||||
margin-bottom: 50px;
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
|
@ -107,7 +107,7 @@
|
||||
|
||||
[data-ref="table"] {
|
||||
margin-top: 1rem;
|
||||
/* margin-bottom: 200px; */
|
||||
margin-bottom: 50px;
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
|
@ -84,7 +84,7 @@
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
/* margin-bottom: 200px; */
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.task-time-details {
|
||||
|
@ -110,7 +110,7 @@
|
||||
padding-left: 2rem;
|
||||
padding-right: 0rem;
|
||||
margin-top: 1rem;
|
||||
/* margin-bottom: 200px; */
|
||||
margin-bottom: 50px;
|
||||
min-width: 100%;
|
||||
table-layout: fixed;
|
||||
overflow-wrap: break-word;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div
|
||||
class="h-screen flex overflow-hidden bg-gray-100"
|
||||
class="main_layout h-screen flex overflow-hidden bg-gray-100"
|
||||
x-data="{ sidebarOpen: false }"
|
||||
@keydown.window.escape="sidebarOpen = false"
|
||||
id="main-sidebar">
|
||||
|
@ -27,6 +27,10 @@
|
||||
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
<div class="alert alert-warning mb-4">
|
||||
<h2>Adding a bank account here requires verification, which may take several days. In order to use Instant Verification please pay an invoice first, this process will automatically verify your bank account.</h2>
|
||||
</div>
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_holder_type')])
|
||||
<span class="flex items-center mr-4">
|
||||
<input class="form-radio mr-2" type="radio" value="individual" name="account-holder-type" checked>
|
||||
@ -39,14 +43,18 @@
|
||||
@endcomponent
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_holder_name')])
|
||||
<input class="input w-full" id="account-holder-name" type="text" placeholder="{{ ctrans('texts.name') }}" required>
|
||||
<input class="input w-full" id="account-holder-name" type="text" placeholder="{{ ctrans('texts.name') }}" required value="{{ auth()->guard('contact')->user()->client->present()->name() }}">
|
||||
@endcomponent
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.country')])
|
||||
<select name="countries" id="country" class="form-select input w-full">
|
||||
<option disabled selected></option>
|
||||
@foreach($countries as $country)
|
||||
@if($country->iso_3166_2 == 'US')
|
||||
<option value="{{ $country->iso_3166_2 }}" selected>{{ $country->iso_3166_2 }} ({{ $country->name }})</option>
|
||||
@else
|
||||
<option value="{{ $country->iso_3166_2 }}">{{ $country->iso_3166_2 }} ({{ $country->name }})</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
@endcomponent
|
||||
@ -55,7 +63,11 @@
|
||||
<select name="currencies" id="currency" class="form-select input w-full">
|
||||
<option disabled selected></option>
|
||||
@foreach($currencies as $currency)
|
||||
<option value="{{ $currency->code }}">{{ $currency->code }} ({{ $currency->name }})</option>
|
||||
@if($currency->code == 'USD')
|
||||
<option value="{{ $currency->code }}" selected>{{ $currency->code }} ({{ $currency->name }})</option>
|
||||
@else
|
||||
<option value="{{ $currency->code }}">{{ $currency->code }} ({{ $currency->name }})</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
@endcomponent
|
||||
|
@ -1,64 +1,227 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH', 'card_title' => 'ACH'])
|
||||
|
||||
@section('gateway_head')
|
||||
@if($gateway->company_gateway->getConfigField('account_id'))
|
||||
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
|
||||
<meta name="stripe-publishable-key" content="{{ config('ninja.ninja_stripe_publishable_key') }}">
|
||||
@else
|
||||
<meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
|
||||
@endif
|
||||
|
||||
<meta name="client_secret" content="{{ $client_secret }}">
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1" />
|
||||
|
||||
@endsection
|
||||
|
||||
@section('gateway_content')
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
|
||||
@csrf
|
||||
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
|
||||
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
|
||||
<input type="hidden" name="source" value="">
|
||||
<input type="hidden" name="amount" value="{{ $amount }}">
|
||||
<input type="hidden" name="currency" value="{{ $currency }}">
|
||||
<input type="hidden" name="customer" value="{{ $customer->id }}">
|
||||
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
|
||||
<input type="hidden" name="client_secret" value="{{ $client_secret }}">
|
||||
<input type="hidden" name="gateway_response" id="gateway_response" value="">
|
||||
<input type="hidden" name="bank_account_response" id="bank_account_response" value="">
|
||||
</form>
|
||||
|
||||
@if(count($tokens) > 0)
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.payment_details')
|
||||
|
||||
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
|
||||
@csrf
|
||||
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
|
||||
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
|
||||
<input type="hidden" name="source" value="">
|
||||
<input type="hidden" name="amount" value="{{ $amount }}">
|
||||
<input type="hidden" name="currency" value="{{ $currency }}">
|
||||
<input type="hidden" name="customer" value="{{ $customer->id }}">
|
||||
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
|
||||
</form>
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
|
||||
@if(count($tokens) > 0)
|
||||
<ul class="list-none hover:list-disc">
|
||||
@foreach($tokens as $token)
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
data-token="{{ $token->hashed_id }}"
|
||||
name="payment-type"
|
||||
class="form-radio cursor-pointer toggle-payment-with-token"/>
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.bank_transfer') }} (*{{ $token->meta->last4 }})</span>
|
||||
</label>
|
||||
<li class="py-1 hover:text-blue hover:bg-blue-600">
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
data-token="{{ $token->hashed_id }}"
|
||||
name="payment-type"
|
||||
class="form-check-input text-indigo-600 rounded-full cursor-pointer toggle-payment-with-token"/>
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.bank_transfer') }} (*{{ $token->meta->last4 }})</span>
|
||||
</label>
|
||||
</li>
|
||||
@endforeach
|
||||
@endisset
|
||||
</ul>
|
||||
@endif
|
||||
@endcomponent
|
||||
|
||||
@else
|
||||
@component('portal.ninja2020.components.general.card-element-single', ['title' => 'ACH', 'show_title' => false])
|
||||
<span>{{ ctrans('texts.bank_account_not_linked') }}</span>
|
||||
<a class="button button-link text-primary"
|
||||
href="{{ route('client.payment_methods.index') }}">{{ ctrans('texts.add_payment_method') }}</a>
|
||||
@endcomponent
|
||||
@endif
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.pay_now')
|
||||
|
||||
@else
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element-single')
|
||||
<input type="checkbox" class="form-checkbox mr-1" id="accept-terms" required>
|
||||
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
|
||||
@endcomponent
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_holder_name')])
|
||||
<input class="input w-full" id="account-holder-name-field" type="text" placeholder="{{ ctrans('texts.name') }}" value="{{ $gateway->client->present()->first_name() }} {{ $gateway->client->present()->last_name(); }}"required>
|
||||
@endcomponent
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.email')])
|
||||
<input class="input w-full" id="email-field" type="text" placeholder="{{ ctrans('texts.email') }}" value="{{ $gateway->client->present()->email(); }}" required>
|
||||
@endcomponent
|
||||
<div class="px-4 py-5 sm:px-6 lg:grid lg:grid-cols-3 lg:gap-4 lg:flex lg:items-center">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500 mr-4">
|
||||
Connect a bank account
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
<button type="button" class="button button-primary bg-primary" id="new-bank" type="button">
|
||||
<svg class="animate-spin h-5 w-5 text-white hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<span>{{ $slot ?? ctrans('texts.new_bank_account') }}</span>
|
||||
</button>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@endsection
|
||||
|
||||
@push('footer')
|
||||
<script>
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
|
||||
<script>
|
||||
|
||||
let payNow = document.getElementById('pay-now');
|
||||
|
||||
if(payNow)
|
||||
{
|
||||
|
||||
Array
|
||||
.from(document.getElementsByClassName('toggle-payment-with-token'))
|
||||
.forEach((element) => element.addEventListener('click', (element) => {
|
||||
document.querySelector('input[name=source]').value = element.target.dataset.token;
|
||||
}));
|
||||
|
||||
document.getElementById('pay-now').addEventListener('click', function () {
|
||||
|
||||
let payNowButton = document.getElementById('pay-now');
|
||||
payNowButton.disabled = true;
|
||||
payNowButton.querySelector('svg').classList.remove('hidden');
|
||||
payNowButton.querySelector('span').classList.add('hidden');
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
payNow.addEventListener('click', function () {
|
||||
let payNowButton = document.getElementById('pay-now');
|
||||
payNowButton.disabled = true;
|
||||
payNowButton.querySelector('svg').classList.remove('hidden');
|
||||
payNowButton.querySelector('span').classList.add('hidden');
|
||||
document.getElementById('server-response').submit();
|
||||
});
|
||||
</script>
|
||||
|
||||
}
|
||||
|
||||
document.getElementById('new-bank').addEventListener('click', (ev) => {
|
||||
|
||||
if (!document.getElementById('accept-terms').checked) {
|
||||
errors.textContent = "You must accept the mandate terms prior to making payment.";
|
||||
errors.hidden = false;
|
||||
return;
|
||||
}
|
||||
|
||||
errors.hidden = true;
|
||||
|
||||
let stripe;
|
||||
|
||||
let publishableKey = document.querySelector('meta[name="stripe-publishable-key"]').content
|
||||
|
||||
let stripeConnect = document.querySelector('meta[name="stripe-account-id"]')?.content
|
||||
|
||||
if(stripeConnect){
|
||||
stripe = Stripe(publishableKey, { stripeAccount: stripeConnect});
|
||||
}
|
||||
else {
|
||||
stripe = Stripe(publishableKey);
|
||||
}
|
||||
|
||||
let newBankButton = document.getElementById('new-bank');
|
||||
newBankButton.disabled = true;
|
||||
newBankButton.querySelector('svg').classList.remove('hidden');
|
||||
newBankButton.querySelector('span').classList.add('hidden');
|
||||
|
||||
ev.preventDefault();
|
||||
const accountHolderNameField = document.getElementById('account-holder-name-field');
|
||||
const emailField = document.getElementById('email-field');
|
||||
const clientSecret = document.querySelector('meta[name="client_secret"]')?.content;
|
||||
// Calling this method will open the instant verification dialog.
|
||||
stripe.collectBankAccountForPayment({
|
||||
clientSecret: clientSecret,
|
||||
params: {
|
||||
payment_method_type: 'us_bank_account',
|
||||
payment_method_data: {
|
||||
billing_details: {
|
||||
name: accountHolderNameField.value,
|
||||
email: emailField.value,
|
||||
},
|
||||
},
|
||||
},
|
||||
expand: ['payment_method'],
|
||||
})
|
||||
.then(({paymentIntent, error}) => {
|
||||
if (error) {
|
||||
|
||||
console.error(error.message);
|
||||
errors.textContent = error.message;
|
||||
errors.hidden = false;
|
||||
resetButtons();
|
||||
|
||||
// PaymentMethod collection failed for some reason.
|
||||
} else if (paymentIntent.status === 'requires_payment_method') {
|
||||
// Customer canceled the hosted verification modal. Present them with other
|
||||
// payment method type options.
|
||||
|
||||
errors.textContent = "We were unable to process the payment with this account, please try another one.";
|
||||
errors.hidden = false;
|
||||
resetButtons();
|
||||
return;
|
||||
|
||||
} else if (paymentIntent.status === 'requires_confirmation') {
|
||||
|
||||
let bank_account_response = document.getElementById('bank_account_response');
|
||||
bank_account_response.value = JSON.stringify(paymentIntent);
|
||||
|
||||
confirmPayment(stripe, clientSecret);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
function confirmPayment(stripe, clientSecret){
|
||||
stripe.confirmUsBankAccountPayment(clientSecret)
|
||||
.then(({paymentIntent, error}) => {
|
||||
console.log(paymentIntent);
|
||||
if (error) {
|
||||
console.error(error.message);
|
||||
// The payment failed for some reason.
|
||||
} else if (paymentIntent.status === "requires_payment_method") {
|
||||
// Confirmation failed. Attempt again with a different payment method.
|
||||
|
||||
errors.textContent = "We were unable to process the payment with this account, please try another one.";
|
||||
errors.hidden = false;
|
||||
resetButtons();
|
||||
|
||||
} else if (paymentIntent.status === "processing") {
|
||||
// Confirmation succeeded! The account will be debited.
|
||||
|
||||
let gateway_response = document.getElementById('gateway_response');
|
||||
gateway_response.value = JSON.stringify(paymentIntent);
|
||||
document.getElementById('server-response').submit();
|
||||
|
||||
} else if (paymentIntent.next_action?.type === "verify_with_microdeposits") {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function resetButtons()
|
||||
{
|
||||
|
||||
let newBankButton = document.getElementById('new-bank');
|
||||
newBankButton.disabled = false;
|
||||
newBankButton.querySelector('svg').classList.add('hidden');
|
||||
newBankButton.querySelector('span').classList.remove('hidden');
|
||||
|
||||
}
|
||||
</script>
|
||||
@endpush
|
@ -0,0 +1,68 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'ACH', 'card_title' => 'ACH'])
|
||||
|
||||
@section('gateway_content')
|
||||
@if(count($tokens) > 0)
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.payment_details')
|
||||
|
||||
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
|
||||
@csrf
|
||||
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
|
||||
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
|
||||
<input type="hidden" name="source" value="">
|
||||
<input type="hidden" name="amount" value="{{ $amount }}">
|
||||
<input type="hidden" name="currency" value="{{ $currency }}">
|
||||
<input type="hidden" name="customer" value="{{ $customer->id }}">
|
||||
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
|
||||
<input type="hidden" name="client_secret" value="{{ $client_secret }}">
|
||||
</form>
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
|
||||
@if(count($tokens) > 0)
|
||||
@foreach($tokens as $token)
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
data-token="{{ $token->hashed_id }}"
|
||||
name="payment-type"
|
||||
class="form-radio cursor-pointer toggle-payment-with-token"/>
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.bank_transfer') }} (*{{ $token->meta->last4 }})</span>
|
||||
</label>
|
||||
@endforeach
|
||||
@endisset
|
||||
@endcomponent
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.pay_now')
|
||||
|
||||
@else
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element-single', ['title' => 'ACH', 'show_title' => false])
|
||||
<span>Pay with a new bank account.</span>
|
||||
<button type="button" class="button button-primary bg-primary" id="new-bank">{{ ctrans('texts.new_bank_account') }}</button>
|
||||
|
||||
@endcomponent
|
||||
|
||||
@endif
|
||||
|
||||
@endsection
|
||||
|
||||
@push('footer')
|
||||
<script>
|
||||
Array
|
||||
.from(document.getElementsByClassName('toggle-payment-with-token'))
|
||||
.forEach((element) => element.addEventListener('click', (element) => {
|
||||
document.querySelector('input[name=source]').value = element.target.dataset.token;
|
||||
}));
|
||||
|
||||
document.getElementById('pay-now').addEventListener('click', function () {
|
||||
|
||||
let payNowButton = document.getElementById('pay-now');
|
||||
payNowButton.disabled = true;
|
||||
payNowButton.querySelector('svg').classList.remove('hidden');
|
||||
payNowButton.querySelector('span').classList.add('hidden');
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
});
|
||||
</script>
|
||||
@endpush
|
@ -11,7 +11,6 @@
|
||||
|
||||
@endphp
|
||||
|
||||
|
||||
@section('gateway_head')
|
||||
@if($gateway->company_gateway->getConfigField('account_id'))
|
||||
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
|
||||
@ -48,32 +47,40 @@
|
||||
@include('portal.ninja2020.gateways.includes.payment_details')
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
|
||||
<ul class="list-none hover:list-disc">
|
||||
@if(count($tokens) > 0)
|
||||
@foreach($tokens as $token)
|
||||
<li class="py-2 hover:text-blue hover:bg-blue-600">
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
data-token="{{ $token->token }}"
|
||||
name="payment-type"
|
||||
class="form-radio cursor-pointer toggle-payment-with-token"/>
|
||||
class="form-check-input text-indigo-600 rounded-full cursor-pointer toggle-payment-with-token toggle-payment-with-token"/>
|
||||
<span class="ml-1 cursor-pointer">**** {{ optional($token->meta)->last4 }}</span>
|
||||
</label>
|
||||
</li>
|
||||
@endforeach
|
||||
@endisset
|
||||
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
id="toggle-payment-with-credit-card"
|
||||
class="form-radio cursor-pointer"
|
||||
name="payment-type"
|
||||
checked/>
|
||||
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
|
||||
</label>
|
||||
<li class="py-2 hover:text-blue hover:bg-blue-600">
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
id="toggle-payment-with-credit-card"
|
||||
class="form-check-input text-indigo-600 rounded-full cursor-pointer"
|
||||
name="payment-type"
|
||||
checked/>
|
||||
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@endcomponent
|
||||
|
||||
@include('portal.ninja2020.gateways.stripe.includes.card_widget')
|
||||
@include('portal.ninja2020.gateways.includes.pay_now')
|
||||
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
|
@ -0,0 +1,57 @@
|
||||
<button id="link-button">Link Account</button>
|
||||
|
||||
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
|
||||
<script type="text/javascript">
|
||||
(async function() {
|
||||
|
||||
const configs = {
|
||||
// Pass the link_token generated in step 2.
|
||||
token: '{{ $link_token }}',
|
||||
onLoad: function() {
|
||||
// The Link module finished loading.
|
||||
},
|
||||
onSuccess: function(public_token, metadata) {
|
||||
// The onSuccess function is called when the user has
|
||||
// successfully authenticated and selected an account to
|
||||
// use.
|
||||
//
|
||||
// When called, you will send the public_token
|
||||
// and the selected account ID, metadata.accounts,
|
||||
// to your backend app server.
|
||||
//
|
||||
// sendDataToBackendServer({
|
||||
// public_token: public_token,
|
||||
// account_id: metadata.accounts[0].id
|
||||
// });
|
||||
console.log('Public Token: ' + public_token);
|
||||
switch (metadata.accounts.length) {
|
||||
case 0:
|
||||
// Select Account is disabled: https://dashboard.plaid.com/link/account-select
|
||||
break;
|
||||
case 1:
|
||||
console.log('Customer-selected account ID: ' + metadata.accounts[0].id);
|
||||
break;
|
||||
default:
|
||||
// Multiple Accounts is enabled: https://dashboard.plaid.com/link/account-select
|
||||
}
|
||||
},
|
||||
onExit: async function(err, metadata) {
|
||||
// The user exited the Link flow.
|
||||
if (err != null) {
|
||||
// The user encountered a Plaid API error
|
||||
// prior to exiting.
|
||||
}
|
||||
// metadata contains information about the institution
|
||||
// that the user selected and the most recent
|
||||
// API request IDs.
|
||||
// Storing this information can be helpful for support.
|
||||
},
|
||||
};
|
||||
|
||||
var linkHandler = Plaid.create(configs);
|
||||
|
||||
document.getElementById('link-button').onclick = function() {
|
||||
linkHandler.open();
|
||||
};
|
||||
})();
|
||||
</script>
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user