From 1d7ba6d1e69fbdcbb5d4751cad0c6a1dd8cc332c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Jul 2020 08:06:28 +1000 Subject: [PATCH 1/7] Fixes for event parameters --- app/Console/Commands/DemoMode.php | 2 ++ app/Http/Controllers/ClientPortal/InvitationController.php | 2 +- app/Listeners/Misc/InvitationViewedListener.php | 4 ++-- app/Mail/Admin/EntityPaidObject.php | 3 ++- app/PaymentDrivers/AuthorizePaymentDriver.php | 1 + app/Utils/Traits/Inviteable.php | 6 +++--- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/Console/Commands/DemoMode.php b/app/Console/Commands/DemoMode.php index c5008f5471ab..b878949ce901 100644 --- a/app/Console/Commands/DemoMode.php +++ b/app/Console/Commands/DemoMode.php @@ -7,6 +7,7 @@ use App\Events\Invoice\InvoiceWasCreated; use App\Factory\InvoiceFactory; use App\Factory\InvoiceItemFactory; use App\Helpers\Invoice\InvoiceSum; +use App\Jobs\Util\VersionCheck; use App\Models\CompanyToken; use App\Models\Country; use App\Models\Product; @@ -77,6 +78,7 @@ class DemoMode extends Command $this->info("Seeding Random Data"); $this->createSmallAccount(); + VersionCheck::dispatchNow(); } diff --git a/app/Http/Controllers/ClientPortal/InvitationController.php b/app/Http/Controllers/ClientPortal/InvitationController.php index 8258c04f5840..ec613405a3b8 100644 --- a/app/Http/Controllers/ClientPortal/InvitationController.php +++ b/app/Http/Controllers/ClientPortal/InvitationController.php @@ -48,7 +48,7 @@ class InvitationController extends Controller if (!request()->has('silent')) { $invitation->markViewed(); - event(new InvitationWasViewed($entity, $invitation, $entity->company, Ninja::eventVars())); + event(new InvitationWasViewed($invitation->{$entity}, $invitation, $invitation->{$entity}->company, Ninja::eventVars())); } return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})]); diff --git a/app/Listeners/Misc/InvitationViewedListener.php b/app/Listeners/Misc/InvitationViewedListener.php index 394474432058..6f6b74227d29 100644 --- a/app/Listeners/Misc/InvitationViewedListener.php +++ b/app/Listeners/Misc/InvitationViewedListener.php @@ -40,9 +40,9 @@ class InvitationViewedListener implements ShouldQueue */ public function handle($event) { - MultiDB::setDb($event->company->db); + MultiDB::setDb($event->company->db); - $entity_name = $event->entity; + $entity_name = lcfirst(class_basename($event->entity)); $invitation = $event->invitation; $notification = new EntityViewedNotification($invitation, $entity_name); diff --git a/app/Mail/Admin/EntityPaidObject.php b/app/Mail/Admin/EntityPaidObject.php index 449931c8db77..237fbc36eeb8 100644 --- a/app/Mail/Admin/EntityPaidObject.php +++ b/app/Mail/Admin/EntityPaidObject.php @@ -85,7 +85,8 @@ class EntityPaidObject 'invoice' => $invoice_texts, ] ), - 'url' => config('ninja.app_url') . '/payments/' . $this->payment->hashed_id, + 'url' => config('ninja.app_url'), + // 'url' => config('ninja.app_url') . '/payments/' . $this->payment->hashed_id, //because we have no deep linking we cannot use this 'button' => ctrans('texts.view_payment'), 'signature' => $settings->email_signature, 'logo' => $this->company->present()->logo(), diff --git a/app/PaymentDrivers/AuthorizePaymentDriver.php b/app/PaymentDrivers/AuthorizePaymentDriver.php index da502665b6f6..26df0695feea 100644 --- a/app/PaymentDrivers/AuthorizePaymentDriver.php +++ b/app/PaymentDrivers/AuthorizePaymentDriver.php @@ -14,6 +14,7 @@ namespace App\PaymentDrivers; use App\Models\ClientGatewayToken; use App\Models\GatewayType; +use App\Models\Invoice; use App\Models\Payment; use App\PaymentDrivers\Authorize\AuthorizeCreditCard; use App\PaymentDrivers\Authorize\AuthorizePaymentMethod; diff --git a/app/Utils/Traits/Inviteable.php b/app/Utils/Traits/Inviteable.php index 2a674d581541..42811e385485 100644 --- a/app/Utils/Traits/Inviteable.php +++ b/app/Utils/Traits/Inviteable.php @@ -55,14 +55,14 @@ trait Inviteable switch ($this->company->portal_mode) { case 'subdomain': - return $domain .'/client/'. $entity_type .'/'. $this->key; + return $domain .'client/'. $entity_type .'/'. $this->key; break; case 'iframe': - return $domain .'/client/'. $entity_type .'/'. $this->key; + return $domain .'client/'. $entity_type .'/'. $this->key; //return $domain . $entity_type .'/'. $this->contact->client->client_hash .'/'. $this->key; break; case 'domain': - return $domain .'/client/'. $entity_type .'/'. $this->key; + return $domain .'client/'. $entity_type .'/'. $this->key; break; } From 00a3d36fcd5c084caeb3acb0a44daf4c1a700277 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Jul 2020 15:37:49 +1000 Subject: [PATCH 2/7] Bug with viewed_date populating erroneously --- .../ClientPortal/InvitationController.php | 6 +- .../Misc/InvitationViewedListener.php | 6 +- app/Models/CreditInvitation.php | 56 ++++++++-------- app/Models/InvoiceInvitation.php | 64 +++++++++++-------- app/Models/QuoteInvitation.php | 56 ++++++++-------- 5 files changed, 100 insertions(+), 88 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/InvitationController.php b/app/Http/Controllers/ClientPortal/InvitationController.php index ec613405a3b8..8061672d3c61 100644 --- a/app/Http/Controllers/ClientPortal/InvitationController.php +++ b/app/Http/Controllers/ClientPortal/InvitationController.php @@ -44,8 +44,10 @@ class InvitationController extends Controller } else { auth()->guard('contact')->login($invitation->contact, true); } - - if (!request()->has('silent')) { + + if (!request()->has('silent') && !$invitation->viewed_date) { +// if (!request()->has('silent')) { + $invitation->markViewed(); event(new InvitationWasViewed($invitation->{$entity}, $invitation, $invitation->{$entity}->company, Ninja::eventVars())); diff --git a/app/Listeners/Misc/InvitationViewedListener.php b/app/Listeners/Misc/InvitationViewedListener.php index 6f6b74227d29..501655e20698 100644 --- a/app/Listeners/Misc/InvitationViewedListener.php +++ b/app/Listeners/Misc/InvitationViewedListener.php @@ -46,6 +46,7 @@ class InvitationViewedListener implements ShouldQueue $invitation = $event->invitation; $notification = new EntityViewedNotification($invitation, $entity_name); + $notification_not_fired_yet = true; foreach ($invitation->company->company_users as $company_user) { @@ -53,11 +54,12 @@ class InvitationViewedListener implements ShouldQueue $methods = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed]); - if (($key = array_search('mail', $methods)) !== false) { + if (($key = array_search('mail', $methods)) !== false && $notification_not_fired_yet) { unset($methods[$key]); EntityViewedMailer::dispatch($invitation, $entity_name, $company_user->user, $invitation->company); - + $notification_not_fired_yet = false; + } $notification->method = $methods; diff --git a/app/Models/CreditInvitation.php b/app/Models/CreditInvitation.php index 3220a568711f..c1171e752d9a 100644 --- a/app/Models/CreditInvitation.php +++ b/app/Models/CreditInvitation.php @@ -42,37 +42,37 @@ class CreditInvitation extends BaseModel return CreditInvitation::class; } - public function getSignatureDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getSignatureDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } - public function getSentDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getSentDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } - public function getViewedDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getViewedDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } - public function getOpenedDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getOpenedDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } public function entityType() { diff --git a/app/Models/InvoiceInvitation.php b/app/Models/InvoiceInvitation.php index e41a310d13b6..57675418b247 100644 --- a/app/Models/InvoiceInvitation.php +++ b/app/Models/InvoiceInvitation.php @@ -41,37 +41,37 @@ class InvoiceInvitation extends BaseModel return InvoiceInvitation::class; } - public function getSignatureDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getSignatureDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } - public function getSentDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getSentDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } - public function getViewedDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getViewedDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } - public function getOpenedDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getOpenedDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } public function entityType() { @@ -126,10 +126,18 @@ class InvoiceInvitation extends BaseModel public function markViewed() { + info('marking viewed here'); + $this->viewed_date = Carbon::now(); $this->save(); } + public function markOpened() + { + $this->opened_date = Carbon::now(); + $this->save(); + } + public function pdf_file_path() { $storage_path = Storage::url($this->invoice->client->invoice_filepath() . $this->invoice->number . '.pdf'); diff --git a/app/Models/QuoteInvitation.php b/app/Models/QuoteInvitation.php index b8b37cda9227..4558f489943e 100644 --- a/app/Models/QuoteInvitation.php +++ b/app/Models/QuoteInvitation.php @@ -36,37 +36,37 @@ class QuoteInvitation extends BaseModel return QuoteInvitation::class; } - public function getSignatureDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getSignatureDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } - public function getSentDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getSentDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } - public function getViewedDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getViewedDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } - public function getOpenedDateAttribute($value) - { - if (!$value) { - return (new Carbon($value))->format('Y-m-d'); - } - return $value; - } + // public function getOpenedDateAttribute($value) + // { + // if (!$value) { + // return (new Carbon($value))->format('Y-m-d'); + // } + // return $value; + // } public function entityType() { From fe4885f5a6f27dbf44fd360d46419c65647ec8ba Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Jul 2020 19:55:28 +1000 Subject: [PATCH 3/7] only send one notification per invoice --- app/Http/Controllers/EmailController.php | 8 ++++++-- app/Http/Controllers/SetupController.php | 7 ++++++- app/Jobs/Mail/EntitySentMailer.php | 1 - app/Jobs/Mail/EntityViewedMailer.php | 2 -- app/Listeners/Invoice/InvoiceEmailedNotification.php | 5 ++++- app/Listeners/Misc/InvitationViewedListener.php | 4 +--- app/Models/InvoiceInvitation.php | 2 -- app/Notifications/Admin/InvoiceSentNotification.php | 2 +- app/Notifications/Admin/InvoiceViewedNotification.php | 2 +- app/Notifications/Admin/NewPartialPaymentNotification.php | 2 +- app/Notifications/Admin/NewPaymentNotification.php | 2 +- app/Utils/HtmlEngine.php | 3 ++- app/Utils/Traits/MakesInvoiceValues.php | 3 ++- resources/views/layouts/guest.blade.php | 2 +- resources/views/layouts/master.blade.php | 4 ++-- resources/views/portal/ninja2020/layout/clean.blade.php | 2 +- 16 files changed, 29 insertions(+), 22 deletions(-) diff --git a/app/Http/Controllers/EmailController.php b/app/Http/Controllers/EmailController.php index 62760c5acce3..8bfa7ffef5c4 100644 --- a/app/Http/Controllers/EmailController.php +++ b/app/Http/Controllers/EmailController.php @@ -122,11 +122,15 @@ class EmailController extends BaseController $invitation->contact->notify((new SendGenericNotification($invitation, $entity_string, $subject, $body))->delay($when)); - EntitySentMailer::dispatch($invitation, $entity_string, $entity_obj->user, $invitation->company); - } }); + + /*Only notify the admin ONCE, not once per contact/invite*/ + $invitation = $entity_obj->invitations->first(); + + EntitySentMailer::dispatch($invitation, $entity_string, $entity_obj->user, $invitation->company); + if ($this instanceof Invoice) { $this->entity_type = Invoice::class ; diff --git a/app/Http/Controllers/SetupController.php b/app/Http/Controllers/SetupController.php index 43a8378fd324..84f3fdd6325d 100644 --- a/app/Http/Controllers/SetupController.php +++ b/app/Http/Controllers/SetupController.php @@ -62,8 +62,13 @@ class SetupController extends Controller $mail_driver = 'log'; } + $url = $request->input('url'); + + if(substr($url, -1) != '/') + $url = $url . '/'; + $_ENV['APP_KEY'] = config('app.key'); - $_ENV['APP_URL'] = $request->input('url'); + $_ENV['APP_URL'] = $url; $_ENV['APP_DEBUG'] = $request->input('debug') ? 'true' : 'false'; $_ENV['REQUIRE_HTTPS'] = $request->input('https') ? 'true' : 'false'; $_ENV['DB_TYPE'] = 'mysql'; diff --git a/app/Jobs/Mail/EntitySentMailer.php b/app/Jobs/Mail/EntitySentMailer.php index 2eee1d4d6022..9680851a35bd 100644 --- a/app/Jobs/Mail/EntitySentMailer.php +++ b/app/Jobs/Mail/EntitySentMailer.php @@ -56,7 +56,6 @@ class EntitySentMailer extends BaseMailerJob implements ShouldQueue */ public function handle() { - info("entity sent mailer"); //Set DB MultiDB::setDb($this->company->db); diff --git a/app/Jobs/Mail/EntityViewedMailer.php b/app/Jobs/Mail/EntityViewedMailer.php index c5fd76e36dd4..be1102a948f9 100644 --- a/app/Jobs/Mail/EntityViewedMailer.php +++ b/app/Jobs/Mail/EntityViewedMailer.php @@ -57,8 +57,6 @@ class EntityViewedMailer extends BaseMailerJob implements ShouldQueue public function handle() { - info("entity viewed mailer"); - //Set DB MultiDB::setDb($this->company->db); diff --git a/app/Listeners/Invoice/InvoiceEmailedNotification.php b/app/Listeners/Invoice/InvoiceEmailedNotification.php index 5673afb03778..ec20f01f79d4 100644 --- a/app/Listeners/Invoice/InvoiceEmailedNotification.php +++ b/app/Listeners/Invoice/InvoiceEmailedNotification.php @@ -44,6 +44,7 @@ class InvoiceEmailedNotification implements ShouldQueue { MultiDB::setDb($event->company->db); + $first_notification_sent = true; foreach ($invitation->company->company_users as $company_user) { @@ -53,7 +54,7 @@ class InvoiceEmailedNotification implements ShouldQueue $methods = $this->findUserNotificationTypes($invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent']); - if (($key = array_search('mail', $methods)) !== false) { + if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) { unset($methods[$key]); //Fire mail notification here!!! @@ -61,6 +62,8 @@ class InvoiceEmailedNotification implements ShouldQueue //handle the mailer EntitySentMailer::dispatch($invitation, 'invoice', $user, $invitation->company); + $first_notification_sent = false; + } $notification->method = $methods; diff --git a/app/Listeners/Misc/InvitationViewedListener.php b/app/Listeners/Misc/InvitationViewedListener.php index 501655e20698..1997c2baf4b5 100644 --- a/app/Listeners/Misc/InvitationViewedListener.php +++ b/app/Listeners/Misc/InvitationViewedListener.php @@ -46,7 +46,6 @@ class InvitationViewedListener implements ShouldQueue $invitation = $event->invitation; $notification = new EntityViewedNotification($invitation, $entity_name); - $notification_not_fired_yet = true; foreach ($invitation->company->company_users as $company_user) { @@ -54,11 +53,10 @@ class InvitationViewedListener implements ShouldQueue $methods = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed]); - if (($key = array_search('mail', $methods)) !== false && $notification_not_fired_yet) { + if (($key = array_search('mail', $methods)) !== false) { unset($methods[$key]); EntityViewedMailer::dispatch($invitation, $entity_name, $company_user->user, $invitation->company); - $notification_not_fired_yet = false; } diff --git a/app/Models/InvoiceInvitation.php b/app/Models/InvoiceInvitation.php index 57675418b247..466ec9fd7583 100644 --- a/app/Models/InvoiceInvitation.php +++ b/app/Models/InvoiceInvitation.php @@ -126,8 +126,6 @@ class InvoiceInvitation extends BaseModel public function markViewed() { - info('marking viewed here'); - $this->viewed_date = Carbon::now(); $this->save(); } diff --git a/app/Notifications/Admin/InvoiceSentNotification.php b/app/Notifications/Admin/InvoiceSentNotification.php index 245d4f6d852a..100d6e0200fb 100644 --- a/app/Notifications/Admin/InvoiceSentNotification.php +++ b/app/Notifications/Admin/InvoiceSentNotification.php @@ -85,7 +85,7 @@ class InvoiceSentNotification extends Notification implements ShouldQueue 'invoice' => $this->invoice->number, ] ), - 'url' => config('ninja.app_url') . '/invoices/' . $this->invoice->hashed_id, + 'url' => config('ninja.app_url') . 'invoices/' . $this->invoice->hashed_id, 'button' => ctrans('texts.view_invoice'), 'signature' => $this->settings->email_signature, 'logo' => $this->company->present()->logo(), diff --git a/app/Notifications/Admin/InvoiceViewedNotification.php b/app/Notifications/Admin/InvoiceViewedNotification.php index 5bc49983f311..f1249299189b 100644 --- a/app/Notifications/Admin/InvoiceViewedNotification.php +++ b/app/Notifications/Admin/InvoiceViewedNotification.php @@ -85,7 +85,7 @@ class InvoiceViewedNotification extends Notification implements ShouldQueue 'invoice' => $this->invoice->number, ] ), - 'url' => config('ninja.app_url') . '/invoices/' . $this->invoice->hashed_id, + 'url' => config('ninja.app_url') . 'invoices/' . $this->invoice->hashed_id, 'button' => ctrans('texts.view_invoice'), 'signature' => $this->settings->email_signature, 'logo' => $this->company->present()->logo(), diff --git a/app/Notifications/Admin/NewPartialPaymentNotification.php b/app/Notifications/Admin/NewPartialPaymentNotification.php index 5298eb5ce921..384857195af1 100644 --- a/app/Notifications/Admin/NewPartialPaymentNotification.php +++ b/app/Notifications/Admin/NewPartialPaymentNotification.php @@ -83,7 +83,7 @@ class NewPartialPaymentNotification extends Notification implements ShouldQueue 'invoice' => $invoice_texts, ] ), - 'url' => config('ninja.app_url') . '/payments/' . $this->payment->hashed_id, + 'url' => config('ninja.app_url') . 'payments/' . $this->payment->hashed_id, 'button' => ctrans('texts.view_payment'), 'signature' => $this->settings->email_signature, 'logo' => $this->company->present()->logo(), diff --git a/app/Notifications/Admin/NewPaymentNotification.php b/app/Notifications/Admin/NewPaymentNotification.php index ca0c77f368fa..f3e3ee30233a 100644 --- a/app/Notifications/Admin/NewPaymentNotification.php +++ b/app/Notifications/Admin/NewPaymentNotification.php @@ -86,7 +86,7 @@ class NewPaymentNotification extends Notification implements ShouldQueue 'invoice' => $invoice_texts, ] ), - 'url' => config('ninja.app_url') . '/payments/' . $this->payment->hashed_id, + 'url' => config('ninja.app_url') . 'payments/' . $this->payment->hashed_id, 'button' => ctrans('texts.view_payment'), 'signature' => $this->settings->email_signature, 'logo' => $this->company->present()->logo(), diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index 337c170d45ea..fc922bc5f3cd 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -496,7 +496,8 @@ class HtmlEngine */ public function generateAppUrl() { - return rtrim(config('ninja.app_url'), "/"); + //return rtrim(config('ninja.app_url'), "/"); + return config('ninja.app_url'); } /** diff --git a/app/Utils/Traits/MakesInvoiceValues.php b/app/Utils/Traits/MakesInvoiceValues.php index c824707954c3..626241f57894 100644 --- a/app/Utils/Traits/MakesInvoiceValues.php +++ b/app/Utils/Traits/MakesInvoiceValues.php @@ -749,7 +749,8 @@ trait MakesInvoiceValues */ public function generateAppUrl() { - return rtrim(config('ninja.app_url'), "/"); + //return rtrim(config('ninja.app_url'), "/"); + return config('ninja.app_url'); } /** diff --git a/resources/views/layouts/guest.blade.php b/resources/views/layouts/guest.blade.php index 6db093859999..8187713a24e0 100644 --- a/resources/views/layouts/guest.blade.php +++ b/resources/views/layouts/guest.blade.php @@ -36,7 +36,7 @@ - + diff --git a/resources/views/layouts/master.blade.php b/resources/views/layouts/master.blade.php index 72325ca5ffae..0928ce4dbbea 100644 --- a/resources/views/layouts/master.blade.php +++ b/resources/views/layouts/master.blade.php @@ -35,7 +35,7 @@ - + --/> @@ -57,7 +57,7 @@ - + diff --git a/resources/views/portal/ninja2020/layout/clean.blade.php b/resources/views/portal/ninja2020/layout/clean.blade.php index e6862eacd91b..8ad112620787 100644 --- a/resources/views/portal/ninja2020/layout/clean.blade.php +++ b/resources/views/portal/ninja2020/layout/clean.blade.php @@ -54,7 +54,7 @@ {{-- --}} - + {{-- Feel free to push anything to header using @push('header') --}} @stack('head') From dc63e533df9ecc2bc3de2d78070a959702df779c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Jul 2020 20:35:41 +1000 Subject: [PATCH 4/7] Fix for quote event --- app/Events/Quote/QuoteWasUpdated.php | 2 ++ app/Jobs/Mail/EntityPaidMailer.php | 1 - app/Models/QuoteInvitation.php | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Events/Quote/QuoteWasUpdated.php b/app/Events/Quote/QuoteWasUpdated.php index d6ccf8933bd7..c1ffc0de2817 100644 --- a/app/Events/Quote/QuoteWasUpdated.php +++ b/app/Events/Quote/QuoteWasUpdated.php @@ -11,6 +11,8 @@ namespace App\Events\Quote; +use App\Models\Company; +use App\Models\Quote; use Illuminate\Queue\SerializesModels; /** diff --git a/app/Jobs/Mail/EntityPaidMailer.php b/app/Jobs/Mail/EntityPaidMailer.php index a2175f2043c2..573d0a0c5f17 100644 --- a/app/Jobs/Mail/EntityPaidMailer.php +++ b/app/Jobs/Mail/EntityPaidMailer.php @@ -53,7 +53,6 @@ class EntityPaidMailer extends BaseMailerJob implements ShouldQueue */ public function handle() { - info("entity paid mailer"); //Set DB // MultiDB::setDb($this->company->db); diff --git a/app/Models/QuoteInvitation.php b/app/Models/QuoteInvitation.php index 4558f489943e..5bef00af9fe9 100644 --- a/app/Models/QuoteInvitation.php +++ b/app/Models/QuoteInvitation.php @@ -125,7 +125,7 @@ class QuoteInvitation extends BaseModel $storage_path = Storage::url($this->quote->client->quote_filepath() . $this->quote->number . '.pdf'); if (!Storage::exists($this->quote->client->quote_filepath() . $this->quote->number . '.pdf')) { - event(new QuoteWasUpdated($this, $this->company, Ninja::eventVars())); + event(new QuoteWasUpdated($this->quote, $this->company, Ninja::eventVars())); CreateQuotePdf::dispatchNow($this); } From d8a13e6cb257fb9f133f99e432284843b076a19e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Jul 2020 22:50:16 +1000 Subject: [PATCH 5/7] Auto billing for stripe --- .../Invoice/InvoiceEmailedNotification.php | 8 +-- .../Authorize/AuthorizeCreditCard.php | 4 +- app/PaymentDrivers/AuthorizePaymentDriver.php | 2 +- app/PaymentDrivers/Stripe/Charge.php | 50 +++++++++++++++++++ app/PaymentDrivers/StripePaymentDriver.php | 6 ++- app/Services/Invoice/AutoBillInvoice.php | 4 +- 6 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 app/PaymentDrivers/Stripe/Charge.php diff --git a/app/Listeners/Invoice/InvoiceEmailedNotification.php b/app/Listeners/Invoice/InvoiceEmailedNotification.php index ec20f01f79d4..d4ad6b320fc9 100644 --- a/app/Listeners/Invoice/InvoiceEmailedNotification.php +++ b/app/Listeners/Invoice/InvoiceEmailedNotification.php @@ -46,13 +46,13 @@ class InvoiceEmailedNotification implements ShouldQueue $first_notification_sent = true; - foreach ($invitation->company->company_users as $company_user) { + foreach ($event->invitation->company->company_users as $company_user) { $user = $company_user->user; - $notification = new EntitySentNotification($invitation, 'invoice'); + $notification = new EntitySentNotification($event->invitation, 'invoice'); - $methods = $this->findUserNotificationTypes($invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent']); + $methods = $this->findUserNotificationTypes($event->invitation, $company_user, 'invoice', ['all_notifications', 'invoice_sent']); if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) { unset($methods[$key]); @@ -61,7 +61,7 @@ class InvoiceEmailedNotification implements ShouldQueue //This allows us better control of how we //handle the mailer - EntitySentMailer::dispatch($invitation, 'invoice', $user, $invitation->company); + EntitySentMailer::dispatch($event->invitation, 'invoice', $user, $event->invitation->company); $first_notification_sent = false; } diff --git a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php index c7767f45e273..f961857f148b 100644 --- a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php +++ b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php @@ -124,10 +124,12 @@ class AuthorizeCreditCard SystemLogger::dispatch($logger_message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_AUTHORIZE, $this->authorize->client); - + return true; } else { + + return false; } } diff --git a/app/PaymentDrivers/AuthorizePaymentDriver.php b/app/PaymentDrivers/AuthorizePaymentDriver.php index 26df0695feea..079dbc7a5852 100644 --- a/app/PaymentDrivers/AuthorizePaymentDriver.php +++ b/app/PaymentDrivers/AuthorizePaymentDriver.php @@ -143,7 +143,7 @@ class AuthorizePaymentDriver extends BaseDriver { $this->setPaymentMethod($cgt->gateway_type_id); - $this->payment_method->tokenBilling($cgt, $amount, $invoice); + return $this->payment_method->tokenBilling($cgt, $amount, $invoice); } } diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php new file mode 100644 index 000000000000..03d7b09c2b0f --- /dev/null +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -0,0 +1,50 @@ +stripe = $stripe; + } + + /** + * Create a charge against a payment method + * @return bool success/failure + */ + public function tokenBilling(ClientGatewayToken $cgt, $amount, ?Invoice $invoice) + { + + if($invoice) + $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; + else + $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; + + $response = $this->stripe->charges->create([ + 'amount' => $this->stripe->convertToStripeAmount($amount, $this->stripe->client->currency()->precision), + 'currency' => $this->stripe->client->getCurrencyCode(), + 'source' => $cgt->token, + 'description' => $description, + ]); + + info(print_r($response,1)); + } + +} diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index d0c96407d0d1..b27967dae0fc 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -27,6 +27,7 @@ use App\Models\PaymentType; use App\Models\SystemLog; use App\PaymentDrivers\Stripe\ACH; use App\PaymentDrivers\Stripe\Alipay; +use App\PaymentDrivers\Stripe\Charge; use App\PaymentDrivers\Stripe\CreditCard; use App\PaymentDrivers\Stripe\SOFORT; use App\PaymentDrivers\Stripe\Utilities; @@ -366,7 +367,10 @@ class StripePaymentDriver extends BasePaymentDriver return response([], 200); } - public function tokenBilling(ClientGatewayToken $cgt, float $amount) {} + public function tokenBilling(ClientGatewayToken $cgt, float $amount, ?Invoice $invoice = null) + { + return (new Charge)->tokenBilling($cgt, $amount, $invoice); + } } diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 30eec874b51c..e82a122a8000 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -47,8 +47,10 @@ class AutoBillInvoice extends AbstractService else return $this->invoice->service()->markPaid()->save(); - if(!$gateway_token) + if(!$gateway_token || !$gateway_token->gateway->driver($this->client)->token_billing){ + info("either no gateway token record OR tokenbilling not implemented"); return $this->invoice; + } if($this->invoice->partial){ $fee = $gateway_token->gateway->calcGatewayFee($this->invoice->partial); From 8c56fc126314ce782bd3121c2917a343f8cfa7c8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Jul 2020 23:15:59 +1000 Subject: [PATCH 6/7] Working on autobill --- app/PaymentDrivers/Stripe/Charge.php | 83 ++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index 03d7b09c2b0f..f44bbebb46d8 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -48,3 +48,86 @@ class Charge } } + +// { +// "id": "ch_1H4lp42eZvKYlo2Ch5igaUwg", +// "object": "charge", +// "amount": 2000, +// "amount_refunded": 0, +// "application": null, +// "application_fee": null, +// "application_fee_amount": null, +// "balance_transaction": "txn_19XJJ02eZvKYlo2ClwuJ1rbA", +// "billing_details": { +// "address": { +// "city": null, +// "country": null, +// "line1": null, +// "line2": null, +// "postal_code": "45465", +// "state": null +// }, +// "email": null, +// "name": null, +// "phone": null +// }, +// "calculated_statement_descriptor": null, +// "captured": false, +// "created": 1594724238, +// "currency": "usd", +// "customer": null, +// "description": "My First Test Charge (created for API docs)", +// "disputed": false, +// "failure_code": null, +// "failure_message": null, +// "fraud_details": {}, +// "invoice": null, +// "livemode": false, +// "metadata": {}, +// "on_behalf_of": null, +// "order": null, +// "outcome": null, +// "paid": true, +// "payment_intent": null, +// "payment_method": "card_1F8MLI2eZvKYlo2CvsyCzps2", +// "payment_method_details": { +// "card": { +// "brand": "visa", +// "checks": { +// "address_line1_check": null, +// "address_postal_code_check": "pass", +// "cvc_check": null +// }, +// "country": "US", +// "exp_month": 12, +// "exp_year": 2023, +// "fingerprint": "Xt5EWLLDS7FJjR1c", +// "funding": "credit", +// "installments": null, +// "last4": "4242", +// "network": "visa", +// "three_d_secure": null, +// "wallet": null +// }, +// "type": "card" +// }, +// "receipt_email": null, +// "receipt_number": null, +// "receipt_url": "https://pay.stripe.com/receipts/acct_1032D82eZvKYlo2C/ch_1H4lp42eZvKYlo2Ch5igaUwg/rcpt_He3wuRQtzvT2Oi4OAYQSpajtmteo55J", +// "refunded": false, +// "refunds": { +// "object": "list", +// "data": [], +// "has_more": false, +// "url": "/v1/charges/ch_1H4lp42eZvKYlo2Ch5igaUwg/refunds" +// }, +// "review": null, +// "shipping": null, +// "source_transfer": null, +// "statement_descriptor": null, +// "statement_descriptor_suffix": null, +// "status": "succeeded", +// "transfer_data": null, +// "transfer_group": null, +// "source": "tok_visa" +// } \ No newline at end of file From cd4856e8db4ee81ccf99d8076d8430c923c3da59 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 15 Jul 2020 15:05:02 +1000 Subject: [PATCH 7/7] Helpers for gateway fees --- app/Models/GatewayType.php | 5 + .../Authorize/AuthorizeCreditCard.php | 1 - app/PaymentDrivers/AuthorizePaymentDriver.php | 4 + app/PaymentDrivers/Stripe/Charge.php | 395 +++++++++++++++++- app/PaymentDrivers/StripePaymentDriver.php | 33 +- app/Services/Invoice/AutoBillInvoice.php | 41 +- app/Services/Invoice/InvoiceService.php | 27 ++ 7 files changed, 477 insertions(+), 29 deletions(-) diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index f87ee10df145..e3e6c5b26a58 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -37,4 +37,9 @@ class GatewayType extends StaticModel { return $this->belongsTo(Gateway::class); } + + public function payment_methods() + { + return $this->hasMany(PaymentType::class); + } } diff --git a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php index f961857f148b..2105ca383d5f 100644 --- a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php +++ b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php @@ -209,7 +209,6 @@ class AuthorizeCreditCard 'code' => $response->getTransactionResponse()->getMessages()[0]->getCode(), 'description' => $response->getTransactionResponse()->getMessages()[0]->getDescription(), 'invoices' => $vars['hashed_ids'], - ]; } diff --git a/app/PaymentDrivers/AuthorizePaymentDriver.php b/app/PaymentDrivers/AuthorizePaymentDriver.php index 079dbc7a5852..f0644e642c07 100644 --- a/app/PaymentDrivers/AuthorizePaymentDriver.php +++ b/app/PaymentDrivers/AuthorizePaymentDriver.php @@ -37,6 +37,10 @@ class AuthorizePaymentDriver extends BaseDriver public $merchant_authentication; + public $token_billing = true; + + public $can_authorise_credit_card = true; + public static $methods = [ GatewayType::CREDIT_CARD => AuthorizeCreditCard::class, ]; diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index f44bbebb46d8..38aa4d0b3d79 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -12,8 +12,14 @@ namespace App\PaymentDrivers\Stripe; +use App\Events\Payment\PaymentWasCreated; +use App\Jobs\Util\SystemLogger; use App\Models\ClientGatewayToken; +use App\Models\Invoice; +use App\Models\PaymentType; +use App\Models\SystemLog; use App\PaymentDrivers\StripePaymentDriver; +use App\Utils\Ninja; class Charge { @@ -37,18 +43,199 @@ class Charge else $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; - $response = $this->stripe->charges->create([ - 'amount' => $this->stripe->convertToStripeAmount($amount, $this->stripe->client->currency()->precision), - 'currency' => $this->stripe->client->getCurrencyCode(), - 'source' => $cgt->token, - 'description' => $description, - ]); + $this->stripe->init(); - info(print_r($response,1)); + $local_stripe = new \Stripe\StripeClient( + $this->stripe->company_gateway->getConfigField('apiKey') + ); + + $response = null; + + try { + + $response = $local_stripe->paymentIntents->create([ + 'amount' => $this->stripe->convertToStripeAmount($amount, $this->stripe->client->currency()->precision), + 'currency' => $this->stripe->client->getCurrencyCode(), + 'payment_method' => $cgt->token, + 'customer' => $cgt->gateway_customer_reference, + 'confirm' => true, + 'description' => $description, + ]); + + SystemLogger::dispatch($response, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client); + + + } catch(\Stripe\Exception\CardException $e) { + // Since it's a decline, \Stripe\Exception\CardException will be caught + + $data = [ + 'status' => $e->getHttpStatus(), + 'error_type' => $e->getError()->type, + 'error_code' => $e->getError()->code, + 'param' => $e->getError()->param, + 'message' => $e->getError()->message + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\RateLimitException $e) { + // Too many requests made to the API too quickly + + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'Too many requests made to the API too quickly' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\InvalidRequestException $e) { + // Invalid parameters were supplied to Stripe's API + // + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'Invalid parameters were supplied to Stripe\'s API' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\AuthenticationException $e) { + // Authentication with Stripe's API failed + + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'Authentication with Stripe\'s API failed' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\ApiConnectionException $e) { + // Network communication with Stripe failed + + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'Network communication with Stripe failed' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (\Stripe\Exception\ApiErrorException $e) { + + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => 'API Error' + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + + } catch (Exception $e) { + // Something else happened, completely unrelated to Stripe + // + $data = [ + 'status' => '', + 'error_type' => '', + 'error_code' => '', + 'param' => '', + 'message' => $e->getMessage(), + ]; + + SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); + } + + if(!$response) + return false; + + $payment_method_type = $response->charges->data[0]->payment_method_details->card->brand; + info($payment_method_type); + + $data = [ + 'gateway_type_id' => $cgt->gateway_type_id, + 'type_id' => $this->transformPaymentTypeToConstant($payment_method_type), + 'transaction_reference' => $response->charges->data[0]->id, + ]; + + $payment = $this->stripe->createPaymentRecord($data, $amount); + + if($invoice) + $this->stripe->attachInvoices($payment, $invoice->hashed_id); + + $payment->service()->updateInvoicePayment(); + + event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars())); + + return $payment; + } + + + private function formatGatewayResponse($data, $vars) + { + $response = $data['response']; + + return [ + 'transaction_reference' => $response->getTransactionResponse()->getTransId(), + 'amount' => $vars['amount'], + 'auth_code' => $response->getTransactionResponse()->getAuthCode(), + 'code' => $response->getTransactionResponse()->getMessages()[0]->getCode(), + 'description' => $response->getTransactionResponse()->getMessages()[0]->getDescription(), + 'invoices' => $vars['hashed_ids'], + ]; + } + + private function transformPaymentTypeToConstant($type) + { + switch ($type) { + case 'visa': + return PaymentType::VISA; + break; + case 'mastercard': + return PaymentType::MASTERCARD; + break; + default: + return PaymentType::CREDIT_CARD_OTHER; + break; + } } } + // const CREDIT = 1; + // const ACH = 4; + // const VISA = 5; + // const MASTERCARD = 6; + // const AMERICAN_EXPRESS = 7; + // const DISCOVER = 8; + // const DINERS = 9; + // const EUROCARD = 10; + // const NOVA = 11; + // const CREDIT_CARD_OTHER = 12; + // const PAYPAL = 13; + // const CARTE_BLANCHE = 16; + // const UNIONPAY = 17; + // const JCB = 18; + // const LASER = 19; + // const MAESTRO = 20; + // const SOLO = 21; + // const SWITCH = 22; + // const ALIPAY = 27; + // const SOFORT = 28; + // const SEPA = 29; + // const GOCARDLESS = 30; + // const CRYPTO = 31; + // { // "id": "ch_1H4lp42eZvKYlo2Ch5igaUwg", // "object": "charge", @@ -130,4 +317,196 @@ class Charge // "transfer_data": null, // "transfer_group": null, // "source": "tok_visa" -// } \ No newline at end of file +// } +// +// +// [2020-07-14 23:06:47] local.INFO: Stripe\PaymentIntent Object +// ( +// [id] => pi_1H4xD0Kmol8YQE9DKhrvV6Nc +// [object] => payment_intent +// [allowed_source_types] => Array +// ( +// [0] => card +// ) + +// [amount] => 1000 +// [amount_capturable] => 0 +// [amount_received] => 1000 +// [application] => +// [application_fee_amount] => +// [canceled_at] => +// [cancellation_reason] => +// [capture_method] => automatic +// [charges] => Stripe\Collection Object +// ( +// [object] => list +// [data] => Array +// ( +// [0] => Stripe\Charge Object +// ( +// [id] => ch_1H4xD0Kmol8YQE9Ds9b1ZWjw +// [object] => charge +// [amount] => 1000 +// [amount_refunded] => 0 +// [application] => +// [application_fee] => +// [application_fee_amount] => +// [balance_transaction] => txn_1H4xD1Kmol8YQE9DE9qFoO0R +// [billing_details] => Stripe\StripeObject Object +// ( +// [address] => Stripe\StripeObject Object +// ( +// [city] => +// [country] => +// [line1] => +// [line2] => +// [postal_code] => 42334 +// [state] => +// ) + +// [email] => +// [name] => sds +// [phone] => +// ) + +// [calculated_statement_descriptor] => NODDY +// [captured] => 1 +// [created] => 1594768006 +// [currency] => usd +// [customer] => cus_He4VEiYldHJWqG +// [description] => Invoice 0023 for 10 for client Corwin Group +// [destination] => +// [dispute] => +// [disputed] => +// [failure_code] => +// [failure_message] => +// [fraud_details] => Array +// ( +// ) + +// [invoice] => +// [livemode] => +// [metadata] => Stripe\StripeObject Object +// ( +// ) + +// [on_behalf_of] => +// [order] => +// [outcome] => Stripe\StripeObject Object +// ( +// [network_status] => approved_by_network +// [reason] => +// [risk_level] => normal +// [risk_score] => 13 +// [seller_message] => Payment complete. +// [type] => authorized +// ) + +// [paid] => 1 +// [payment_intent] => pi_1H4xD0Kmol8YQE9DKhrvV6Nc +// [payment_method] => pm_1H4mNAKmol8YQE9DUMRsuTXs +// [payment_method_details] => Stripe\StripeObject Object +// ( +// [card] => Stripe\StripeObject Object +// ( +// [brand] => visa +// [checks] => Stripe\StripeObject Object +// ( +// [address_line1_check] => +// [address_postal_code_check] => pass +// [cvc_check] => +// ) + +// [country] => US +// [exp_month] => 4 +// [exp_year] => 2024 +// [fingerprint] => oCjEXlb4syFKwgbJ +// [funding] => credit +// [installments] => +// [last4] => 4242 +// [network] => visa +// [three_d_secure] => +// [wallet] => +// ) + +// [type] => card +// ) + +// [receipt_email] => +// [receipt_number] => +// [receipt_url] => https://pay.stripe.com/receipts/acct_19DXXPKmol8YQE9D/ch_1H4xD0Kmol8YQE9Ds9b1ZWjw/rcpt_HeFiiwzRZtnOpvHyohNN5JXtCYe8Rdc +// [refunded] => +// [refunds] => Stripe\Collection Object +// ( +// [object] => list +// [data] => Array +// ( +// ) + +// [has_more] => +// [total_count] => 0 +// [url] => /v1/charges/ch_1H4xD0Kmol8YQE9Ds9b1ZWjw/refunds +// ) + +// [review] => +// [shipping] => +// [source] => +// [source_transfer] => +// [statement_descriptor] => +// [statement_descriptor_suffix] => +// [status] => succeeded +// [transfer_data] => +// [transfer_group] => +// ) + +// ) + +// [has_more] => +// [total_count] => 1 +// [url] => /v1/charges?payment_intent=pi_1H4xD0Kmol8YQE9DKhrvV6Nc +// ) + +// [client_secret] => pi_1H4xD0Kmol8YQE9DKhrvV6Nc_secret_TyE8n3Y3oaMqgqQvXvtKDOnYT +// [confirmation_method] => automatic +// [created] => 1594768006 +// [currency] => usd +// [customer] => cus_He4VEiYldHJWqG +// [description] => Invoice 0023 for 10 for client Corwin Group +// [invoice] => +// [last_payment_error] => +// [livemode] => +// [metadata] => Stripe\StripeObject Object +// ( +// ) + +// [next_action] => +// [next_source_action] => +// [on_behalf_of] => +// [payment_method] => pm_1H4mNAKmol8YQE9DUMRsuTXs +// [payment_method_options] => Stripe\StripeObject Object +// ( +// [card] => Stripe\StripeObject Object +// ( +// [installments] => +// [network] => +// [request_three_d_secure] => automatic +// ) + +// ) + +// [payment_method_types] => Array +// ( +// [0] => card +// ) + +// [receipt_email] => +// [review] => +// [setup_future_usage] => +// [shipping] => +// [source] => +// [statement_descriptor] => +// [statement_descriptor_suffix] => +// [status] => succeeded +// [transfer_data] => +// [transfer_group] => +// ) \ No newline at end of file diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index b27967dae0fc..523d9a894df3 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -42,11 +42,11 @@ class StripePaymentDriver extends BasePaymentDriver { use MakesHash, Utilities; - protected $refundable = true; + public $refundable = true; - protected $token_billing = true; + public $token_billing = true; - protected $can_authorise_credit_card = true; + public $can_authorise_credit_card = true; protected $customer_reference = 'customerReferenceParam'; @@ -369,8 +369,33 @@ class StripePaymentDriver extends BasePaymentDriver public function tokenBilling(ClientGatewayToken $cgt, float $amount, ?Invoice $invoice = null) { - return (new Charge)->tokenBilling($cgt, $amount, $invoice); + return (new Charge($this))->tokenBilling($cgt, $amount, $invoice); } + /** + * Creates a payment record for the given + * data array. + * + * @param array $data An array of payment attributes + * @param float $amount The amount of the payment + * @return Payment The payment object + */ + public function createPaymentRecord($data, $amount) :?Payment + { + $payment = PaymentFactory::create($this->client->company_id, $this->client->user_id); + $payment->client_id = $this->client->id; + $payment->company_gateway_id = $this->company_gateway->id; + $payment->status_id = Payment::STATUS_COMPLETED; + $payment->gateway_type_id = $data['gateway_type_id']; + $payment->type_id = $data['type_id']; + $payment->currency_id = $this->client->getSetting('currency_id'); + $payment->date = Carbon::now(); + $payment->transaction_reference = $data['transaction_reference']; + $payment->amount = $amount; + $payment->client->getNextPaymentNumber($this->client); + $payment->save(); + + return $payment; + } } diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index e82a122a8000..cc8ec5f8d66b 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -48,11 +48,10 @@ class AutoBillInvoice extends AbstractService return $this->invoice->service()->markPaid()->save(); if(!$gateway_token || !$gateway_token->gateway->driver($this->client)->token_billing){ - info("either no gateway token record OR tokenbilling not implemented"); return $this->invoice; } - if($this->invoice->partial){ + if($this->invoice->partial > 0){ $fee = $gateway_token->gateway->calcGatewayFee($this->invoice->partial); $amount = $this->invoice->partial + $fee; } @@ -67,29 +66,39 @@ class AutoBillInvoice extends AbstractService if($fee > 0) $this->addFeeToInvoice($fee); - $response = $gateway_token->gateway->driver($this->client)->tokenBilling($gateway_token, $amount, $this->invoice); + $payment = $gateway_token->gateway->driver($this->client)->tokenBilling($gateway_token, $amount, $this->invoice); - //if response was successful, toggle the fee type_id to paid + if($payment){ + + $this->invoice->service()->toggleFeesPaid()->save(); + + } + else + { + //autobill failed + } + + + return $this->invoice; } + /** + * Harvests a client gateway token which passes the + * necessary filters for an $amount + * + * @param float $amount The amount to charge + * @return ClientGatewayToken The client gateway token + */ private function getGateway($amount) { - // $gateway_tokens = $this->client->gateway_tokens()->orderBy('is_default', 'DESC'); - - // return $gateway_tokens->filter(function ($token) use ($amount){ - - // return $this->validGatewayLimits($token, $amount); - - // })->all()->first(); - - $gateway_tokens = $this->client->gateway_tokens()->orderBy('is_default', 'DESC')->get(); foreach($gateway_tokens as $gateway_token) { - if($this->validGatewayLimits($gateway_token, $amount)) + if($this->validGatewayLimits($gateway_token, $amount)){ return $gateway_token; + } } } @@ -152,7 +161,7 @@ class AutoBillInvoice extends AbstractService $this->invoice->line_items = $new_items; $this->invoice->save(); - $this->invoice = $this->invoice->calc()->getInvoice()->save(); + $this->invoice = $this->invoice->calc()->getInvoice(); if($starting_amount != $this->invoice->amount && $this->invoice->status_id != Invoice::STATUS_DRAFT){ $this->invoice->client->service()->updateBalance($this->invoice->amount - $starting_amount)->save(); @@ -176,7 +185,7 @@ class AutoBillInvoice extends AbstractService if(isset($cg->fees_and_limits)) $fees_and_limits = $cg->fees_and_limits->{"1"}; else - $passes = true; + return true; if ((property_exists($fees_and_limits, 'min_limit')) && $fees_and_limits->min_limit !== null && $amount < $fees_and_limits->min_limit) { info("amount {$amount} less than ". $fees_and_limits->min_limit); diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index c1999ec42edb..40d587733b2a 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -178,6 +178,33 @@ class InvoiceService return $this; } + public function toggleFeesPaid() + { + + $this->invoice->line_items = collect($this->invoice->line_items) + ->where('type_id',3)->map(function ($item) { + + $item->type_id=4; + return $item; + + })->toArray(); + + return $this; + } + + public function removeUnpaidGatewayFees() + { + + $this->invoice->line_items = collect($this->invoice->line_items) + ->reject(function ($item) { + + return $item->type_id == 3; + + })->toArray(); + + return $this; + } + public function clearPartial() { $this->invoice->partial = null;