diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 1d362bd85d1d..e155e65202ff 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -70,6 +70,7 @@ class BaseController extends Controller 'company.clients.contacts', 'company.clients.gateway_tokens', 'company.products', + 'company.recurring_invoices', 'company.invoices.invitations.contact', 'company.invoices.invitations.company', 'company.invoices.documents', @@ -213,6 +214,9 @@ class BaseController extends Controller 'company.products' => function ($query) use ($updated_at) { $query->where('updated_at', '>=', $updated_at); }, + 'company.recurring_invoices'=> function ($query) use ($updated_at) { + $query->where('updated_at', '>=', $updated_at)->with('company'); + }, 'company.invoices'=> function ($query) use ($updated_at) { $query->where('updated_at', '>=', $updated_at)->with('invitations', 'company', 'documents'); }, diff --git a/app/Models/Company.php b/app/Models/Company.php index 8093ab24bd42..54b9c7d7f928 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -29,6 +29,7 @@ use App\Models\Language; use App\Models\Payment; use App\Models\PaymentType; use App\Models\Product; +use App\Models\RecurringInvoice; use App\Models\TaxRate; use App\Models\Timezone; use App\Models\Traits\AccountTrait; @@ -240,6 +241,14 @@ class Company extends BaseModel return $this->hasMany(Invoice::class)->withTrashed(); } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function recurring_invoices() + { + return $this->hasMany(RecurringInvoice::class)->withTrashed(); + } + /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ diff --git a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php index 87fca0a08e7e..e154c5c01250 100644 --- a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php +++ b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php @@ -104,6 +104,9 @@ class AuthorizeCreditCard $payment = $this->createPaymentRecord($data, $amount); $payment->meta = $cgt->meta; $payment->save(); + + $payment_hash->payment_id = $payment->id; + $payment_hash->save(); $this->authorize->attachInvoices($payment, $payment_hash); $payment->service()->updateInvoicePayment($payment_hash); @@ -144,6 +147,9 @@ class AuthorizeCreditCard $amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total; $payment = $this->createPaymentRecord($data, $amount); + + $payment_hash->payment_id = $payment->id; + $payment_hash->save(); $this->authorize->attachInvoices($payment, $payment_hash); diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php index ce9dcc2895c2..45ecfd8bd3e0 100644 --- a/app/PaymentDrivers/CheckoutComPaymentDriver.php +++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php @@ -67,13 +67,20 @@ class CheckoutComPaymentDriver extends BaseDriver ]; } - /** Since with Checkout.com we handle only credit cards, this method should be empty. */ - public function setPaymentMethod($string = null) + /** + * Since with Checkout.com we handle only credit cards, this method should be empty. + * @param $string payment_method string + */ + public function setPaymentMethod($payment_method = null) { return $this; } - public function init() + /** + * Initialize the checkout payment driver + * @return $this + */ + public function init() { $config = [ 'secret' => $this->company_gateway->getConfigField('secretApiKey'), @@ -82,8 +89,15 @@ class CheckoutComPaymentDriver extends BaseDriver ]; $this->gateway = new CheckoutApi($config['secret'], $config['sandbox'], $config['public']); + + return $this; } + /** + * Process different view depending on payment type + * @param int $gateway_type_id The gateway type + * @return string The view string + */ public function viewForType($gateway_type_id) { if ($gateway_type_id == GatewayType::CREDIT_CARD) { @@ -95,11 +109,23 @@ class CheckoutComPaymentDriver extends BaseDriver } } + /** + * Authorization view + * + * @param array $data Payment data array + * @return view Authorization View + */ public function authorizeView($data) { return render('gateways.checkout.authorize'); } + /** + * Payment View + * + * @param array $data Payment data array + * @return view The payment view + */ public function processPaymentView(array $data) { $data['gateway'] = $this; @@ -113,6 +139,12 @@ class CheckoutComPaymentDriver extends BaseDriver return render($this->viewForType($data['payment_method_id']), $data); } + /** + * Process the payment response + * + * @param Request $request The payment request + * @return view The payment response view + */ public function processPaymentResponse($request) { $this->init(); @@ -123,6 +155,7 @@ class CheckoutComPaymentDriver extends BaseDriver 'raw_value' => $request->raw_value, 'currency' => $request->currency, 'payment_hash' =>$request->payment_hash, + 'reference' => $request->payment_hash, ]; $state = array_merge($state, $request->all()); @@ -132,10 +165,12 @@ class CheckoutComPaymentDriver extends BaseDriver $method = new IdSource($state['token']); $payment = new CheckoutPayment($method, $state['currency']); $payment->amount = $state['value']; + $payment->reference = $state['reference']; } else { $method = new TokenSource($state['server_response']->cardToken); $payment = new CheckoutPayment($method, $state['currency']); $payment->amount = $state['value']; + $payment->reference = $state['reference']; if ($this->client->currency()->code === 'EUR') { $payment->{'3ds'} = ['enabled' => true]; @@ -162,6 +197,12 @@ class CheckoutComPaymentDriver extends BaseDriver } } + /** + * Process a successful payment response + * + * @param array $state The state array + * @return view The response + */ public function processSuccessfulPayment($state) { $state['charge_id'] = $state['payment_response']->id; @@ -178,6 +219,9 @@ class CheckoutComPaymentDriver extends BaseDriver $payment = $this->createPayment($data, Payment::STATUS_COMPLETED); $payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$state['payment_hash']])->firstOrFail(); + $payment_hash->payment_id = $payment->id; + $payment_hash->save(); + $this->attachInvoices($payment, $payment_hash); $payment->service()->updateInvoicePayment($payment_hash); @@ -251,17 +295,19 @@ class CheckoutComPaymentDriver extends BaseDriver public function processInternallyFailedPayment($e, $state) { - $message = json_decode($e->getBody()); + $error_message = json_decode($e->getBody()); - PaymentFailureMailer::dispatch($this->client, $message->error_type, $this->client->company, $state['value']); + PaymentFailureMailer::dispatch($this->client, $error_message->message, $this->client->company, $state['value']); $message = [ 'server_response' => $state['server_response'], - 'data' => $message, + 'data' => $e->getBody(), ]; SystemLogger::dispatch($message, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_CHECKOUT, $this->client); - + + //todo push to a error page with the exception message. + throw new \Exception('Failed to process the payment.', 1); } diff --git a/app/PaymentDrivers/PayPalExpressPaymentDriver.php b/app/PaymentDrivers/PayPalExpressPaymentDriver.php index 76b8600efd63..952410f09688 100644 --- a/app/PaymentDrivers/PayPalExpressPaymentDriver.php +++ b/app/PaymentDrivers/PayPalExpressPaymentDriver.php @@ -162,6 +162,10 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver $payment = $this->createPayment($response->getData()); $payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->input('payment_hash')])->firstOrFail(); + + $payment_hash->payment_id = $payment->id; + $payment_hash->save(); + $this->attachInvoices($payment, $payment_hash); $payment->service()->updateInvoicePayment($payment_hash); diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index c92b475f9252..4f76dc3e67d5 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -167,6 +167,9 @@ class Charge $payment->meta = $cgt->meta; $payment->save(); + $payment_hash->payment_id = $payment->id; + $payment_hash->save(); + $this->stripe->attachInvoices($payment, $payment_hash); $payment->service()->updateInvoicePayment($payment_hash); diff --git a/app/PaymentDrivers/Stripe/CreditCard.php b/app/PaymentDrivers/Stripe/CreditCard.php index c52ca0b9e279..af617e791fa2 100644 --- a/app/PaymentDrivers/Stripe/CreditCard.php +++ b/app/PaymentDrivers/Stripe/CreditCard.php @@ -197,6 +197,11 @@ class CreditCard $payment = $this->stripe->createPayment($data, $status = Payment::STATUS_COMPLETED); $payment->meta = $payment_meta; + $payment->save(); + + $payment_hash = $state['payment_hash']; + $payment_hash->payment_id = $payment->id; + $payment_hash->save(); $payment = $this->stripe->attachInvoices($payment, $state['payment_hash']); diff --git a/app/Services/Payment/UpdateInvoicePayment.php b/app/Services/Payment/UpdateInvoicePayment.php index ca7ef1119b6a..60eaeb686c8c 100644 --- a/app/Services/Payment/UpdateInvoicePayment.php +++ b/app/Services/Payment/UpdateInvoicePayment.php @@ -75,7 +75,9 @@ class UpdateInvoicePayment ->updateBalance($paid_amount * -1) ->save(); + info("firing invoice was updated event"); event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars())); + info("fired invoice was updated event"); }); return $this->payment; diff --git a/app/Transformers/CompanyTransformer.php b/app/Transformers/CompanyTransformer.php index bd1de6b65de0..c935afef3791 100644 --- a/app/Transformers/CompanyTransformer.php +++ b/app/Transformers/CompanyTransformer.php @@ -29,6 +29,7 @@ use App\Models\PaymentTerm; use App\Models\Product; use App\Models\Project; use App\Models\Quote; +use App\Models\RecurringInvoice; use App\Models\SystemLog; use App\Models\Task; use App\Models\TaxRate; @@ -40,6 +41,7 @@ use App\Transformers\CompanyTokenTransformer; use App\Transformers\CreditTransformer; use App\Transformers\DocumentTransformer; use App\Transformers\PaymentTermTransformer; +use App\Transformers\RecurringInvoiceTransformer; use App\Transformers\SystemLogTransformer; use App\Transformers\TaskTransformer; use App\Transformers\WebhookTransformer; @@ -70,6 +72,7 @@ class CompanyTransformer extends EntityTransformer 'clients', 'contacts', 'invoices', + 'recurring_invoices', 'tax_rates', 'products', 'country', @@ -247,6 +250,13 @@ class CompanyTransformer extends EntityTransformer return $this->includeCollection($company->invoices, $transformer, Invoice::class); } + public function includeRecurringInvoices(Company $company) + { + $transformer = new RecurringInvoiceTransformer($this->serializer); + + return $this->includeCollection($company->recurring_invoices, $transformer, RecurringInvoice::class); + } + public function includeQuotes(Company $company) { $transformer = new QuoteTransformer($this->serializer); diff --git a/app/Transformers/RecurringInvoiceTransformer.php b/app/Transformers/RecurringInvoiceTransformer.php index 51c01e458e8a..71e5586119eb 100644 --- a/app/Transformers/RecurringInvoiceTransformer.php +++ b/app/Transformers/RecurringInvoiceTransformer.php @@ -115,10 +115,10 @@ class RecurringInvoiceTransformer extends EntityTransformer 'footer' => $invoice->footer ?: '', 'partial' => (float) ($invoice->partial ?: 0.0), 'partial_due_date' => $invoice->partial_due_date ?: '', - 'custom_value1' => (float) $invoice->custom_value1 ?: '', - 'custom_value2' => (float) $invoice->custom_value2 ?: '', - 'custom_value3' => (bool) $invoice->custom_value3 ?: '', - 'custom_value4' => (bool) $invoice->custom_value4 ?: '', + 'custom_value1' => (string) $invoice->custom_value1 ?: '', + 'custom_value2' => (string) $invoice->custom_value2 ?: '', + 'custom_value3' => (string) $invoice->custom_value3 ?: '', + 'custom_value4' => (string) $invoice->custom_value4 ?: '', 'has_tasks' => (bool) $invoice->has_tasks, 'has_expenses' => (bool) $invoice->has_expenses, 'custom_surcharge1' => (float) $invoice->custom_surcharge1, diff --git a/composer.lock b/composer.lock index e1d3b8c13ee7..267ae13c98ac 100644 --- a/composer.lock +++ b/composer.lock @@ -160,16 +160,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.152.1", + "version": "3.153.0", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "65e10d45a3ecfdc112b7efe9f839343179a86879" + "reference": "3045bc6c8f7d2521b3e0d4a7b407194af8725c8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/65e10d45a3ecfdc112b7efe9f839343179a86879", - "reference": "65e10d45a3ecfdc112b7efe9f839343179a86879", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3045bc6c8f7d2521b3e0d4a7b407194af8725c8f", + "reference": "3045bc6c8f7d2521b3e0d4a7b407194af8725c8f", "shasum": "" }, "require": { @@ -241,7 +241,7 @@ "s3", "sdk" ], - "time": "2020-09-08T18:13:04+00:00" + "time": "2020-09-09T18:12:35+00:00" }, { "name": "brick/math", @@ -529,16 +529,16 @@ }, { "name": "composer/composer", - "version": "1.10.12", + "version": "1.10.13", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "a6cc92b39c796ec3fb9a360f9c0e578afc2fc96d" + "reference": "47c841ba3b2d3fc0b4b13282cf029ea18b66d78b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/a6cc92b39c796ec3fb9a360f9c0e578afc2fc96d", - "reference": "a6cc92b39c796ec3fb9a360f9c0e578afc2fc96d", + "url": "https://api.github.com/repos/composer/composer/zipball/47c841ba3b2d3fc0b4b13282cf029ea18b66d78b", + "reference": "47c841ba3b2d3fc0b4b13282cf029ea18b66d78b", "shasum": "" }, "require": { @@ -619,7 +619,7 @@ "type": "tidelift" } ], - "time": "2020-09-08T20:58:51+00:00" + "time": "2020-09-09T09:46:34+00:00" }, { "name": "composer/package-versions-deprecated", @@ -692,16 +692,16 @@ }, { "name": "composer/semver", - "version": "1.6.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "9787c20e39dfeea673665abee0679c73ba67105d" + "reference": "114f819054a2ea7db03287f5efb757e2af6e4079" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/9787c20e39dfeea673665abee0679c73ba67105d", - "reference": "9787c20e39dfeea673665abee0679c73ba67105d", + "url": "https://api.github.com/repos/composer/semver/zipball/114f819054a2ea7db03287f5efb757e2af6e4079", + "reference": "114f819054a2ea7db03287f5efb757e2af6e4079", "shasum": "" }, "require": { @@ -763,7 +763,7 @@ "type": "tidelift" } ], - "time": "2020-09-08T20:42:08+00:00" + "time": "2020-09-09T09:34:06+00:00" }, { "name": "composer/spdx-licenses", @@ -2585,16 +2585,16 @@ }, { "name": "laravel/framework", - "version": "v7.28.0", + "version": "v7.28.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "5d3b4e3e62294716e3c810be3d2eb1375685b87d" + "reference": "f7493ab717ca2a9598b1db2d6a3bae8ac8c755e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/5d3b4e3e62294716e3c810be3d2eb1375685b87d", - "reference": "5d3b4e3e62294716e3c810be3d2eb1375685b87d", + "url": "https://api.github.com/repos/laravel/framework/zipball/f7493ab717ca2a9598b1db2d6a3bae8ac8c755e8", + "reference": "f7493ab717ca2a9598b1db2d6a3bae8ac8c755e8", "shasum": "" }, "require": { @@ -2739,7 +2739,7 @@ "framework", "laravel" ], - "time": "2020-09-08T15:10:40+00:00" + "time": "2020-09-09T15:02:46+00:00" }, { "name": "laravel/slack-notification-channel", @@ -2929,16 +2929,16 @@ }, { "name": "laravel/ui", - "version": "v2.2.1", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/laravel/ui.git", - "reference": "456daa330a32483b0fa9794334e60af6b2db3bf6" + "reference": "2ccaa3b821ea8ac7e05393b946d0578bdb46099b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/ui/zipball/456daa330a32483b0fa9794334e60af6b2db3bf6", - "reference": "456daa330a32483b0fa9794334e60af6b2db3bf6", + "url": "https://api.github.com/repos/laravel/ui/zipball/2ccaa3b821ea8ac7e05393b946d0578bdb46099b", + "reference": "2ccaa3b821ea8ac7e05393b946d0578bdb46099b", "shasum": "" }, "require": { @@ -2980,7 +2980,7 @@ "laravel", "ui" ], - "time": "2020-09-08T13:09:39+00:00" + "time": "2020-09-09T12:07:59+00:00" }, { "name": "league/commonmark", diff --git a/database/migrations/2020_08_18_140557_add_is_public_to_documents_table.php b/database/migrations/2020_08_18_140557_add_is_public_to_documents_table.php index cc58b5309c1e..73e37c195f27 100644 --- a/database/migrations/2020_08_18_140557_add_is_public_to_documents_table.php +++ b/database/migrations/2020_08_18_140557_add_is_public_to_documents_table.php @@ -40,7 +40,11 @@ class AddIsPublicToDocumentsTable extends Migration $table->decimal('fee_total', 16, 4); $table->unsignedInteger('fee_invoice_id')->nullable(); $table->mediumText('data'); + $table->unsignedInteger('payment_id')->nullable(); $table->timestamps(6); + + $table->foreign('payment_id')->references('id')->on('payments')->onDelete('cascade')->onUpdate('cascade'); + }); Schema::table('recurring_invoices', function ($table) { diff --git a/resources/views/portal/ninja2020/gateways/checkout/credit_card.blade.php b/resources/views/portal/ninja2020/gateways/checkout/credit_card.blade.php index 26da86c3b343..0c0f85f5fdd7 100644 --- a/resources/views/portal/ninja2020/gateways/checkout/credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/checkout/credit_card.blade.php @@ -6,6 +6,7 @@ + @endpush @@ -15,6 +16,7 @@ @csrf +