diff --git a/app/Console/Commands/PostUpdate.php b/app/Console/Commands/PostUpdate.php index f3892d921d96..4f861e1988e4 100644 --- a/app/Console/Commands/PostUpdate.php +++ b/app/Console/Commands/PostUpdate.php @@ -53,6 +53,7 @@ class PostUpdate extends Command nlog("finished migrating"); exec('vendor/bin/composer install --no-dev'); + exec('vendor/bin/composer dump'); nlog("finished running composer install "); diff --git a/app/Jobs/Entity/EmailEntity.php b/app/Jobs/Entity/EmailEntity.php index 7a291470e2c6..a84a034ddd78 100644 --- a/app/Jobs/Entity/EmailEntity.php +++ b/app/Jobs/Entity/EmailEntity.php @@ -111,7 +111,7 @@ class EmailEntity extends BaseMailerJob implements ShouldQueue ->send( new TemplateEmail( $this->email_entity_builder, - $this->invitation->contact->client + $this->invitation->contact ) ); } catch (\Exception $e) { diff --git a/app/Jobs/Mail/ClientPaymentFailureMailer.php b/app/Jobs/Mail/ClientPaymentFailureMailer.php new file mode 100644 index 000000000000..7db3617995cd --- /dev/null +++ b/app/Jobs/Mail/ClientPaymentFailureMailer.php @@ -0,0 +1,111 @@ +company = $company; + + $this->error = $error; + + $this->client = $client; + + $this->payment_hash = $payment_hash; + + $this->company = $company; + + $this->settings = $client->getMergedSettings(); + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + + /*If we are migrating data we don't want to fire these notification*/ + if ($this->company->is_disabled) { + return true; + } + + //Set DB + MultiDB::setDb($this->company->db); + + //if we need to set an email driver do it now + $this->setMailDriver(); + + $this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get(); + + $this->invoices->first()->invitations->each(function ($invitation) { + + if ($invitation->contact->send_email && $invitation->contact->email) { + + $mail_obj = (new ClientPaymentFailureObject($this->client, $this->error, $this->company, $this->payment_hash))->build(); + $mail_obj->from = [config('mail.from.address'), config('mail.from.name')]; + + //send email + try { + Mail::to($invitation->contact->email) + ->send(new EntityNotificationMailer($mail_obj)); + } catch (\Exception $e) { + + $this->logMailError($e->getMessage(), $this->client); + } + + } + + }); + + + } +} diff --git a/app/Jobs/Mail/PaymentFailureMailer.php b/app/Jobs/Mail/PaymentFailureMailer.php index e51ebe468719..2d78364df648 100644 --- a/app/Jobs/Mail/PaymentFailureMailer.php +++ b/app/Jobs/Mail/PaymentFailureMailer.php @@ -31,11 +31,11 @@ class PaymentFailureMailer extends BaseMailerJob implements ShouldQueue public $client; - public $message; + public $error; public $company; - public $amount; + public $payment_hash; public $settings; @@ -47,15 +47,15 @@ class PaymentFailureMailer extends BaseMailerJob implements ShouldQueue * @param $company * @param $amount */ - public function __construct($client, $message, $company, $amount) + public function __construct($client, $error, $company, $payment_hash) { $this->company = $company; - $this->message = $message; + $this->error = $error; $this->client = $client; - $this->amount = $amount; + $this->payment_hash = $payment_hash; $this->company = $company; @@ -92,7 +92,7 @@ class PaymentFailureMailer extends BaseMailerJob implements ShouldQueue if (($key = array_search('mail', $methods)) !== false) { unset($methods[$key]); - $mail_obj = (new PaymentFailureObject($this->client, $this->message, $this->amount, $this->company))->build(); + $mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $this->payment_hash))->build(); $mail_obj->from = [config('mail.from.address'), config('mail.from.name')]; //send email diff --git a/app/Jobs/Payment/EmailPayment.php b/app/Jobs/Payment/EmailPayment.php index 32d9c36395c2..2530636ee993 100644 --- a/app/Jobs/Payment/EmailPayment.php +++ b/app/Jobs/Payment/EmailPayment.php @@ -80,7 +80,7 @@ class EmailPayment extends BaseMailerJob implements ShouldQueue try { $mail = Mail::to($this->contact->email, $this->contact->present()->name()); - $mail->send(new TemplateEmail($email_builder, $this->contact->client)); + $mail->send(new TemplateEmail($email_builder, $this->contact)); } catch (\Exception $e) { nlog("mailing failed with message " . $e->getMessage()); event(new PaymentWasEmailedAndFailed($this->payment, $this->company, Mail::failures(), Ninja::eventVars())); diff --git a/app/Mail/Admin/AutoBillingFailureObject.php b/app/Mail/Admin/AutoBillingFailureObject.php index e6cfd14103b5..bd30395f67de 100644 --- a/app/Mail/Admin/AutoBillingFailureObject.php +++ b/app/Mail/Admin/AutoBillingFailureObject.php @@ -28,7 +28,7 @@ class AutoBillingFailureObject public $payment_hash; - private $invoice; + private $invoices; /** * Create a new job instance. @@ -54,8 +54,7 @@ class AutoBillingFailureObject public function build() { - - $this->invoice = Invoice::where('id', $this->decodePrimarykey($this->payment_hash->invoices()[0]->invoice_id))->first(); + $this->$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get(); $mail_obj = new stdClass; $mail_obj->amount = $this->getAmount(); @@ -78,7 +77,7 @@ class AutoBillingFailureObject return ctrans( 'texts.auto_bill_failed', - ['invoice_number' => $this->invoice->number] + ['invoice_number' => $this->invoices->first()->number] ); } @@ -89,7 +88,7 @@ class AutoBillingFailureObject $data = [ 'title' => ctrans( 'texts.auto_bill_failed', - ['invoice_number' => $this->invoice->number] + ['invoice_number' => $this->invoices->first()->number] ), 'message' => $this->error, 'signature' => $signature, diff --git a/app/Mail/Admin/ClientPaymentFailureObject.php b/app/Mail/Admin/ClientPaymentFailureObject.php new file mode 100644 index 000000000000..0135a2dad1ab --- /dev/null +++ b/app/Mail/Admin/ClientPaymentFailureObject.php @@ -0,0 +1,114 @@ +client = $client; + + $this->error = $error; + + $this->company = $company; + + $this->payment_hash = $payment_hash; + + $this->company = $company; + + } + + public function build() + { + + $this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get(); + + $mail_obj = new stdClass; + $mail_obj->amount = $this->getAmount(); + $mail_obj->subject = $this->getSubject(); + $mail_obj->data = $this->getData(); + $mail_obj->markdown = 'email.admin.generic'; + $mail_obj->tag = $this->company->company_key; + + return $mail_obj; + } + + private function getAmount() + { + + return array_sum(array_column($this->payment_hash->invoices(), 'amount')) + $this->payment_hash->fee_total; + + } + + private function getSubject() + { + + return + ctrans( + 'texts.notification_invoice_payment_failed_subject', + ['invoice' => $this->client->present()->name()] + ); + + } + + private function getData() + { + $signature = $this->client->getSetting('email_signature'); + + $data = [ + 'title' => ctrans( + 'texts.notification_invoice_payment_failed_subject', + [ + 'invoice' => $this->invoices->first()->number + ] + ), + 'greeting' => ctrans('texts.email_salutation', ['name' => $this->client->present()->name]), + 'message' => $this->error, + 'signature' => $signature, + 'logo' => $this->company->present()->logo(), + 'settings' => $this->client->getMergedSettings(), + 'whitelabel' => $this->company->account->isPaid() ? true : false, + 'url' => route('client.login'), + 'button' => ctrans('texts.login'), + 'additional_info' => false + ]; + + return $data; + } + + +} diff --git a/app/Mail/Admin/PaymentFailureObject.php b/app/Mail/Admin/PaymentFailureObject.php index b7e693d00bfe..3c199f8e491e 100644 --- a/app/Mail/Admin/PaymentFailureObject.php +++ b/app/Mail/Admin/PaymentFailureObject.php @@ -11,29 +11,52 @@ namespace App\Mail\Admin; +use App\Models\Invoice; use App\Utils\Number; +use App\Utils\Traits\MakesHash; use stdClass; class PaymentFailureObject { + use MakesHash; + public $client; - public $message; + public $error; public $company; - public $amount; + public $payment_hash; - public function __construct($client, $message, $amount, $company) + private $invoices; + + /** + * Create a new job instance. + * + * @param $client + * @param $message + * @param $company + * @param $amount + */ + public function __construct($client, $error, $company, $payment_hash) { $this->client = $client; - $this->message = $message; - $this->amount = $amount; + + $this->error = $error; + $this->company = $company; + + $this->payment_hash = $payment_hash; + + $this->company = $company; + } public function build() { + + $this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get(); + $mail_obj = new stdClass; $mail_obj->amount = $this->getAmount(); $mail_obj->subject = $this->getSubject(); @@ -46,16 +69,20 @@ class PaymentFailureObject private function getAmount() { - return Number::formatMoney($this->amount, $this->client); + + return array_sum(array_column($this->payment_hash->invoices(), 'amount')) + $this->payment_hash->fee_total; + } private function getSubject() { + return ctrans( 'texts.payment_failed_subject', ['client' => $this->client->present()->name()] ); + } private function getData() @@ -65,23 +92,36 @@ class PaymentFailureObject $data = [ 'title' => ctrans( 'texts.payment_failed_subject', - ['client' => $this->client->present()->name()] - ), - 'message' => ctrans( - 'texts.notification_payment_paid', - ['amount' => $this->getAmount(), - 'client' => $this->client->present()->name(), - 'message' => $this->message, - ] + [ + 'client' => $this->client->present()->name() + ] ), + 'message' => $this->error, 'signature' => $signature, 'logo' => $this->company->present()->logo(), 'settings' => $this->client->getMergedSettings(), 'whitelabel' => $this->company->account->isPaid() ? true : false, 'url' => config('ninja.app_url'), 'button' => ctrans('texts.login'), + 'additional_info' => $this->buildFailedInvoices() ]; return $data; } + + private function buildFailedInvoices() + { + + $text = ''; + + foreach($this->invoices as $invoice) + { + + $text .= ctrans('texts.notification_invoice_payment_failed_subject', ['invoice' => $invoice->number]) . "\n"; + + } + + return $text; + + } } diff --git a/app/Mail/DownloadInvoices.php b/app/Mail/DownloadInvoices.php index db96f489bd94..8ac8f0db97aa 100644 --- a/app/Mail/DownloadInvoices.php +++ b/app/Mail/DownloadInvoices.php @@ -31,14 +31,14 @@ class DownloadInvoices extends Mailable public function build() { return $this->from(config('mail.from.address'), config('mail.from.name')) - - ->subject(ctrans('texts.download_files')) - ->markdown( - 'email.admin.download_files', - [ - 'url' => $this->file_path, - 'logo' => $this->company->present()->logo, - ] - ); + ->subject(ctrans('texts.download_files')) + ->markdown( + 'email.admin.download_files', + [ + 'url' => $this->file_path, + 'logo' => $this->company->present()->logo, + 'whitelabel' => $this->company->account->isPaid() ? true : false, + ] + ); } } diff --git a/app/Mail/MigrationCompleted.php b/app/Mail/MigrationCompleted.php index 54f158ca9af6..53a5d7f786be 100644 --- a/app/Mail/MigrationCompleted.php +++ b/app/Mail/MigrationCompleted.php @@ -32,7 +32,8 @@ class MigrationCompleted extends Mailable { $data['settings'] = $this->company->settings; $data['company'] = $this->company; - + $data['whitelabel'] = $this->company->account->isPaid() ? true : false; + return $this->from(config('mail.from.address'), config('mail.from.name')) ->view('email.import.completed', $data) ->attach($this->company->invoices->first()->pdf_file_path()); diff --git a/app/Mail/MigrationFailed.php b/app/Mail/MigrationFailed.php index b37e20ce7254..438caf049be4 100644 --- a/app/Mail/MigrationFailed.php +++ b/app/Mail/MigrationFailed.php @@ -32,7 +32,6 @@ class MigrationFailed extends Mailable public function build() { return $this->from(config('mail.from.address'), config('mail.from.name')) - ->view('email.migration.failed'); } } diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php index 42d9a5273c1b..cfe1fa87b10c 100644 --- a/app/Mail/TemplateEmail.php +++ b/app/Mail/TemplateEmail.php @@ -12,6 +12,7 @@ namespace App\Mail; use App\Models\Client; +use App\Models\ClientContact; use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; @@ -25,11 +26,15 @@ class TemplateEmail extends Mailable private $client; - public function __construct($build_email, Client $client) + private $contact; + + public function __construct($build_email, ClientContact $contact) { $this->build_email = $build_email; - $this->client = $client; + $this->contact = $contact; + + $this->client = $contact->client; } /** @@ -64,12 +69,12 @@ class TemplateEmail extends Mailable 'settings' => $settings, ]) ->view($template_name, [ + 'greeting' => ctrans('texts.email_salutation', ['name' => $this->contact->present()->name()]), 'body' => $this->build_email->getBody(), 'footer' => $this->build_email->getFooter(), 'view_link' => $this->build_email->getViewLink(), 'view_text' => $this->build_email->getViewText(), 'title' => '', - // 'title' => $this->build_email->getSubject(), 'signature' => $settings->email_signature, 'settings' => $settings, 'company' => $company, diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index 9b7dea308e3d..3f4169ae6dfc 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -17,6 +17,7 @@ use App\Exceptions\PaymentFailed; use App\Factory\PaymentFactory; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use App\Jobs\Mail\AutoBillingFailureMailer; +use App\Jobs\Mail\ClientPaymentFailureMailer; use App\Jobs\Mail\PaymentFailureMailer; use App\Jobs\Util\SystemLogger; use App\Models\Client; @@ -335,6 +336,8 @@ class BaseDriver extends AbstractPaymentDriver public function processInternallyFailedPayment($gateway, $e) { + $this->unWindGatewayFees($this->payment_hash); + if ($e instanceof CheckoutHttpException) { $error = $e->getBody(); } @@ -344,7 +347,14 @@ class BaseDriver extends AbstractPaymentDriver else $error = $e->getMessage(); - AutoBillingFailureMailer::dispatch( + PaymentFailureMailer::dispatch( + $gateway->client, + $error, + $gateway->client->company, + $this->payment_hash + ); + + ClientPaymentFailureMailer::dispatch( $gateway->client, $error, $gateway->client->company, @@ -362,36 +372,6 @@ class BaseDriver extends AbstractPaymentDriver throw new PaymentFailed($error, $e->getCode()); } - public function tokenBillingFailed($gateway, $e) - { - $this->unWindGatewayFees($this->payment_hash); - - if ($e instanceof CheckoutHttpException) { - $error = $e->getBody(); - } - else if ($e instanceof Exception) { - $error = $e->getMessage(); - } - else - $error = $e->getMessage(); - - AutoBillingFailureMailer::dispatch( - $gateway->client, - $error, - $gateway->client->company, - $this->payment_hash - ); - - SystemLogger::dispatch( - $gateway->payment_hash, - SystemLog::CATEGORY_GATEWAY_RESPONSE, - SystemLog::EVENT_GATEWAY_ERROR, - $gateway::SYSTEM_LOG_TYPE, - $gateway->client, - ); - - } - /** * Wrapper method for checking if resource is good. * diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index 44d6642f850a..84900b720d38 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -79,102 +79,47 @@ class Charge ]); SystemLogger::dispatch($response, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client); - } catch (CardException $e) { - // Since it's a decline, \Stripe\Exception\CardException will be caught + } catch (\Exception $e) { - $data = [ - 'status' => $e->getHttpStatus(), - 'error_type' => $e->getError()->type, - 'error_code' => $e->getError()->code, - 'param' => $e->getError()->param, - 'message' => $e->getError()->message, - ]; - $this->stripe->tokenBillingFailed($this->stripe, $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); - } catch (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', - ]; - - $this->stripe->tokenBillingFailed($this->stripe, $e); - - SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); - } catch (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', - ]; - - $this->stripe->tokenBillingFailed($this->stripe, $e); - - SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); - } catch (AuthenticationException $e) { - // Authentication with Stripe's API failed - - $data = [ - 'status' => '', - 'error_type' => '', - 'error_code' => '', - 'param' => '', - 'message' => 'Authentication with Stripe\'s API failed', - ]; - - $this->stripe->tokenBillingFailed($this->stripe, $e); - - SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); - } catch (ApiConnectionException $e) { - // Network communication with Stripe failed - - $data = [ - 'status' => '', - 'error_type' => '', - 'error_code' => '', - 'param' => '', - 'message' => 'Network communication with Stripe failed', - ]; - - $this->stripe->tokenBillingFailed($this->stripe, $e); - - SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); - } catch (ApiErrorException $e) { - $data = [ - 'status' => '', - 'error_type' => '', - 'error_code' => '', - 'param' => '', - 'message' => 'API Error', - ]; - - $this->stripe->tokenBillingFailed($this->stripe, $e); - - 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(), - ]; - - $this->stripe->tokenBillingFailed($this->stripe, $e); - - SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->stripe->client); - } + } if (! $response) { return false; diff --git a/app/Utils/PhantomJS/Phantom.php b/app/Utils/PhantomJS/Phantom.php index 2c157db6241b..d34f24961026 100644 --- a/app/Utils/PhantomJS/Phantom.php +++ b/app/Utils/PhantomJS/Phantom.php @@ -193,6 +193,9 @@ class Phantom ->build() ->getCompiledHTML(true); + if (config('ninja.log_pdf_html')) { + info($data['html']); + } return view('pdf.html', $data); } diff --git a/resources/views/email/admin/download_files.blade.php b/resources/views/email/admin/download_files.blade.php index b1c86d2b10b0..d1f58b786d25 100644 --- a/resources/views/email/admin/download_files.blade.php +++ b/resources/views/email/admin/download_files.blade.php @@ -16,10 +16,11 @@ InvoiceNinja (contact@invoiceninja.com) @endslot -@slot('footer') - @component('email.components.footer', ['url' => 'https://invoiceninja.com', 'url_text' => '© InvoiceNinja']) - For any info, please visit InvoiceNinja. - @endcomponent -@endslot - +@if(!$whitelabel) + @slot('footer') + @component('email.components.footer', ['url' => 'https://invoiceninja.com', 'url_text' => '© InvoiceNinja']) + For any info, please visit InvoiceNinja. + @endcomponent + @endslot +@endif @endcomponent \ No newline at end of file diff --git a/resources/views/email/admin/generic.blade.php b/resources/views/email/admin/generic.blade.php index bf867eefc32e..fb438dc4ef06 100644 --- a/resources/views/email/admin/generic.blade.php +++ b/resources/views/email/admin/generic.blade.php @@ -1,14 +1,23 @@ @component('email.template.master', ['design' => 'light', 'settings' => $settings]) - @slot('header') @include('email.components.header', ['logo' => $logo]) @endslot + @if(isset($greeting)) +
{{ $greeting }}
+ @endif +{{ $title }}
{{ $message }}
+ @if(isset($additional_info)) + +{{ $additional_info }}
+ + @endif + @component('email.components.button', ['url' => $url]) @lang($button) @endcomponent @@ -17,9 +26,11 @@ {{ $signature }} @endslot - @slot('footer') - @component('email.components.footer', ['url' => 'https://invoiceninja.com', 'url_text' => '© InvoiceNinja']) - For any info, please visit InvoiceNinja. - @endcomponent - @endslot + @if(!$whitelabel) + @slot('footer') + @component('email.components.footer', ['url' => 'https://invoiceninja.com', 'url_text' => '© InvoiceNinja']) + For any info, please visit InvoiceNinja. + @endcomponent + @endslot + @endif @endcomponent diff --git a/resources/views/email/import/completed.blade.php b/resources/views/email/import/completed.blade.php index 1eb858798fa9..91c63b136012 100644 --- a/resources/views/email/import/completed.blade.php +++ b/resources/views/email/import/completed.blade.php @@ -76,4 +76,12 @@ {{ ctrans('texts.account_login')}}{{ ctrans('texts.email_signature')}}
{{ ctrans('texts.email_from') }}